Merge "Adding UNPROCESSED to the available audio_sources and SUPPORT_AUDIO_SOURCE_UNPROCESSED property"
diff --git a/Android.mk b/Android.mk
index d3e1cf5..282b2af 100644
--- a/Android.mk
+++ b/Android.mk
@@ -209,6 +209,7 @@
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
 	core/java/android/os/ICancellationSignal.aidl \
 	core/java/android/os/IDeviceIdleController.aidl \
+	core/java/android/os/IMaintenanceActivityListener.aidl \
 	core/java/android/os/IMessenger.aidl \
 	core/java/android/os/INetworkActivityListener.aidl \
 	core/java/android/os/INetworkManagementService.aidl \
@@ -226,7 +227,6 @@
 	core/java/android/service/carrier/ICarrierMessagingService.aidl \
 	core/java/android/service/gatekeeper/IGateKeeperService.aidl \
 	core/java/android/service/notification/INotificationListener.aidl \
-	core/java/android/service/notification/INotificationAssistant.aidl \
 	core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
 	core/java/android/service/notification/IConditionListener.aidl \
 	core/java/android/service/notification/IConditionProvider.aidl \
@@ -262,7 +262,7 @@
 	core/java/android/view/IApplicationToken.aidl \
 	core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \
 	core/java/android/view/IAssetAtlas.aidl \
-	core/java/android/view/IDockDividerVisibilityListener.aidl \
+	core/java/android/view/IDockedStackListener.aidl \
 	core/java/android/view/IGraphicsStats.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
@@ -342,10 +342,12 @@
 	media/java/android/media/IAudioRoutesObserver.aidl \
 	media/java/android/media/IMediaHTTPConnection.aidl \
 	media/java/android/media/IMediaHTTPService.aidl \
+	media/java/android/media/IMediaResourceMonitor.aidl \
 	media/java/android/media/IMediaRouterClient.aidl \
 	media/java/android/media/IMediaRouterService.aidl \
 	media/java/android/media/IMediaScannerListener.aidl \
 	media/java/android/media/IMediaScannerService.aidl \
+	media/java/android/media/IRecordingConfigDispatcher.aidl \
 	media/java/android/media/IRemoteDisplayCallback.aidl \
 	media/java/android/media/IRemoteDisplayProvider.aidl \
 	media/java/android/media/IRemoteVolumeController.aidl \
@@ -408,10 +410,14 @@
 	telephony/java/com/android/internal/telephony/ISms.aidl \
 	telephony/java/com/android/internal/telephony/ISub.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
+	telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
+	wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \
+	wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
+	wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
 	wifi/java/android/net/wifi/IWifiScanner.aidl \
 	wifi/java/android/net/wifi/IRttManager.aidl \
@@ -422,7 +428,9 @@
 	core/java/android/service/quicksettings/IQSTileService.aidl \
 
 # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
-LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
+LOCAL_AIDL_INCLUDES += \
+      $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
+      frameworks/native/aidl/binder
 
 LOCAL_INTERMEDIATE_SOURCES := \
 			$(framework_res_source_path)/android/R.java \
@@ -480,6 +488,11 @@
 	frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \
 	frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
 	frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/ConfigRequest.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/PublishData.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/SubscribeData.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/PublishSettings.aidl \
+	frameworks/base/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
 	frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
@@ -522,7 +535,6 @@
 	frameworks/base/core/java/android/os/ParcelUuid.aidl \
 	frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
 	frameworks/base/core/java/android/os/ResultReceiver.aidl \
-	frameworks/base/core/java/android/os/PersistableBundle.aidl \
 	frameworks/base/core/java/android/os/WorkSource.aidl \
 	frameworks/base/core/java/android/os/DropBoxManager.aidl \
 	frameworks/base/core/java/android/os/Bundle.aidl \
@@ -630,6 +642,7 @@
 	frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \
 	frameworks/base/core/java/android/database/CursorWindow.aidl \
 	frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
+	frameworks/native/aidl/binder/android/os/PersistableBundle.aidl \
 
 gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
 $(gen): PRIVATE_SRC_FILES := $(aidl_files)
diff --git a/api/current.txt b/api/current.txt
index 06808b0..6e03e0f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -67,7 +67,6 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
-    field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT";
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
@@ -335,6 +334,7 @@
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844040; // 0x1010508
+    field public static final int canPerformGestures = 16844046; // 0x101050e
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
     field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -524,6 +524,7 @@
     field public static final int expandableListViewStyle = 16842863; // 0x101006f
     field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
     field public static final int exported = 16842768; // 0x1010010
+    field public static final int externalService = 16844047; // 0x101050f
     field public static final int extraTension = 16843371; // 0x101026b
     field public static final int extractNativeLibs = 16844010; // 0x10104ea
     field public static final int factor = 16843219; // 0x10101d3
@@ -1289,6 +1290,9 @@
     field public static final int thumbTint = 16843889; // 0x1010471
     field public static final int thumbTintMode = 16843890; // 0x1010472
     field public static final int thumbnail = 16843429; // 0x10102a5
+    field public static final int tickMark = 16844043; // 0x101050b
+    field public static final int tickMarkTint = 16844044; // 0x101050c
+    field public static final int tickMarkTintMode = 16844045; // 0x101050d
     field public static final int tileMode = 16843265; // 0x1010201
     field public static final int tileModeX = 16843895; // 0x1010477
     field public static final int tileModeY = 16843896; // 0x1010478
@@ -2548,6 +2552,7 @@
     field public static final int Widget_Material_ScrollView = 16974462; // 0x103027e
     field public static final int Widget_Material_SearchView = 16974463; // 0x103027f
     field public static final int Widget_Material_SeekBar = 16974464; // 0x1030280
+    field public static final int Widget_Material_SeekBar_Discrete = 16974553; // 0x10302d9
     field public static final int Widget_Material_SegmentedButton = 16974465; // 0x1030281
     field public static final int Widget_Material_Spinner = 16974467; // 0x1030283
     field public static final int Widget_Material_Spinner_Underlined = 16974468; // 0x1030284
@@ -2607,6 +2612,7 @@
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -2646,6 +2652,12 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
 
+  public static abstract class AccessibilityService.GestureResultCallback {
+    ctor public AccessibilityService.GestureResultCallback();
+    method public void onCancelled(android.accessibilityservice.GestureDescription);
+    method public void onCompleted(android.accessibilityservice.GestureDescription);
+  }
+
   public static final class AccessibilityService.MagnificationController {
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
@@ -2678,6 +2690,7 @@
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
+    field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
     field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2704,6 +2717,30 @@
     field public java.lang.String[] packageNames;
   }
 
+  public final class GestureDescription {
+    method public static android.accessibilityservice.GestureDescription createClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
+    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
+    method public int getStrokeCount();
+    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
+    field public static final int MAX_STROKE_COUNT = 10; // 0xa
+  }
+
+  public static class GestureDescription.Builder {
+    ctor public GestureDescription.Builder();
+    method public android.accessibilityservice.GestureDescription.Builder addStroke(android.accessibilityservice.GestureDescription.StrokeDescription);
+    method public android.accessibilityservice.GestureDescription build();
+  }
+
+  public static class GestureDescription.StrokeDescription {
+    ctor public GestureDescription.StrokeDescription(android.graphics.Path, long, long);
+    method public long getDuration();
+    method public android.graphics.Path getPath();
+    method public long getStartTime();
+  }
+
 }
 
 package android.accounts {
@@ -2714,15 +2751,12 @@
     method public android.os.Bundle addAccountFromCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
-    method public android.os.Bundle finishSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public android.os.Bundle getAccountCredentialsForCloning(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
     method public android.os.Bundle getAccountRemovalAllowed(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
     method public final android.os.IBinder getIBinder();
     method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
   }
@@ -2760,7 +2794,6 @@
     method public void clearPassword(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(java.lang.String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
-    method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public static android.accounts.AccountManager get(android.content.Context);
     method public android.accounts.Account[] getAccounts();
     method public android.accounts.Account[] getAccountsByType(java.lang.String);
@@ -2788,8 +2821,6 @@
     method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
     method public void setPassword(android.accounts.Account, java.lang.String);
     method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
-    method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
-    method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
     field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2806,8 +2837,6 @@
     field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
     field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
     field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
-    field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
-    field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
     field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
     field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
     field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3469,6 +3498,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
@@ -3639,6 +3669,9 @@
     method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
   }
 
+  public static abstract class ActivityManager.BugreportMode implements java.lang.annotation.Annotation {
+  }
+
   public static class ActivityManager.MemoryInfo implements android.os.Parcelable {
     ctor public ActivityManager.MemoryInfo();
     method public int describeContents();
@@ -4150,6 +4183,7 @@
     method public void onPanelClosed(int, android.view.Menu);
     method public boolean onPrepareOptionsMenu(android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public void onRestoreInstanceState(android.os.Bundle);
     method public android.os.Bundle onSaveInstanceState();
     method public boolean onSearchRequested(android.view.SearchEvent);
@@ -4229,7 +4263,7 @@
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_ID = "_id";
     field public static final java.lang.String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
-    field public static final java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
+    field public static final deprecated java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
     field public static final java.lang.String COLUMN_LOCAL_URI = "local_uri";
     field public static final java.lang.String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
     field public static final java.lang.String COLUMN_MEDIA_TYPE = "media_type";
@@ -5198,6 +5232,7 @@
     field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff
     field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
+    field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4
     field public final int priorityCallSenders;
     field public final int priorityCategories;
     field public final int priorityMessageSenders;
@@ -5746,6 +5781,7 @@
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
+    method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
@@ -5753,14 +5789,17 @@
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public java.lang.String getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
+    method public java.lang.String getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
+    method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
     method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -5778,6 +5817,7 @@
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
+    method public java.lang.String getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5806,6 +5846,7 @@
     method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
+    method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
@@ -5814,11 +5855,13 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
     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);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
+    method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5843,6 +5886,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
@@ -5858,6 +5902,7 @@
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
+    field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
@@ -6121,6 +6166,7 @@
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.PersistableBundle getExtras();
+    method public long getFlexMillis();
     method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
@@ -6138,6 +6184,8 @@
     field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
     field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6151,6 +6199,7 @@
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
+    method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -6211,6 +6260,8 @@
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
+    method public int getMetering();
+    method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
     method public long getStartTimeStamp();
@@ -6218,6 +6269,12 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int METERING_ALL = -1; // 0xffffffff
+    field public static final int METERING_DEFAULT = 1; // 0x1
+    field public static final int METERING_METERED = 2; // 0x2
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
+    field public static final int ROAMING_DEFAULT = 1; // 0x1
+    field public static final int ROAMING_ROAMING = 2; // 0x2
     field public static final int STATE_ALL = -1; // 0xffffffff
     field public static final int STATE_DEFAULT = 1; // 0x1
     field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -7144,6 +7201,15 @@
     field public static final int TYPE_SCO = 2; // 0x2
   }
 
+  public class OobData implements android.os.Parcelable {
+    ctor public OobData();
+    method public int describeContents();
+    method public byte[] getSecurityManagerTk();
+    method public void setSecurityManagerTk(byte[]);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+  }
+
 }
 
 package android.bluetooth.le {
@@ -7568,11 +7634,12 @@
     method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
   }
 
-  public class ContentProviderClient {
+  public class ContentProviderClient implements java.lang.AutoCloseable {
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
     method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+    method public void close();
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
     method public android.content.ContentProvider getLocalContentProvider();
     method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -7586,7 +7653,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
-    method public boolean release();
+    method public deprecated boolean release();
     method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
   }
@@ -9430,8 +9497,6 @@
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract byte[] getEphemeralCookie();
-    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9441,8 +9506,10 @@
     method public abstract java.lang.String getNameForUid(int);
     method public android.content.pm.PackageInfo getPackageArchiveInfo(java.lang.String, int);
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
+    method public abstract int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] getPackagesForUid(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -9463,7 +9530,6 @@
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
-    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9481,7 +9547,6 @@
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -9513,6 +9578,7 @@
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+    field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
@@ -9571,8 +9637,8 @@
     field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
     field public static final int GET_ACTIVITIES = 1; // 0x1
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
-    field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
-    field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+    field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
+    field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
     field public static final int GET_GIDS = 256; // 0x100
     field public static final int GET_INSTRUMENTATION = 16; // 0x10
     field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -9584,13 +9650,17 @@
     field public static final int GET_SERVICES = 4; // 0x4
     field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
     field public static final int GET_SIGNATURES = 64; // 0x40
-    field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
+    field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+    field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
+    field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+    field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000
     field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000
-    field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000
-    field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000
+    field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000
+    field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
+    field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -10840,7 +10910,7 @@
     field public final int statusCode;
   }
 
-  public class DrmManagerClient {
+  public class DrmManagerClient implements java.lang.AutoCloseable {
     ctor public DrmManagerClient(android.content.Context);
     method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
     method public int acquireRights(android.drm.DrmInfoRequest);
@@ -10850,6 +10920,7 @@
     method public int checkRightsStatus(android.net.Uri);
     method public int checkRightsStatus(java.lang.String, int);
     method public int checkRightsStatus(android.net.Uri, int);
+    method public void close();
     method public android.drm.DrmConvertedStatus closeConvertSession(int);
     method public android.drm.DrmConvertedStatus convertData(int, byte[]);
     method public java.lang.String[] getAvailableDrmEngines();
@@ -10863,7 +10934,7 @@
     method public java.lang.String getOriginalMimeType(android.net.Uri);
     method public int openConvertSession(java.lang.String);
     method public int processDrmInfo(android.drm.DrmInfo);
-    method public void release();
+    method public deprecated void release();
     method public int removeAllRights();
     method public int removeRights(java.lang.String);
     method public int removeRights(android.net.Uri);
@@ -11267,14 +11338,14 @@
 
   public static class BitmapFactory.Options {
     ctor public BitmapFactory.Options();
-    method public void requestCancelDecode();
+    method public deprecated void requestCancelDecode();
     field public android.graphics.Bitmap inBitmap;
     field public int inDensity;
-    field public boolean inDither;
+    field public deprecated boolean inDither;
     field public deprecated boolean inInputShareable;
     field public boolean inJustDecodeBounds;
     field public boolean inMutable;
-    field public boolean inPreferQualityOverSpeed;
+    field public deprecated boolean inPreferQualityOverSpeed;
     field public android.graphics.Bitmap.Config inPreferredConfig;
     field public boolean inPremultiplied;
     field public deprecated boolean inPurgeable;
@@ -11283,7 +11354,7 @@
     field public int inScreenDensity;
     field public int inTargetDensity;
     field public byte[] inTempStorage;
-    field public boolean mCancel;
+    field public deprecated boolean mCancel;
     field public int outHeight;
     field public java.lang.String outMimeType;
     field public int outWidth;
@@ -12722,6 +12793,7 @@
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
+    field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
   }
 
   public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -18243,7 +18315,6 @@
     method public static java.lang.String getCanonicalID(java.lang.String, boolean[]);
     method public int getDSTSavings();
     method public static android.icu.util.TimeZone getDefault();
-    method public static int getDefaultTimeZoneType();
     method public final java.lang.String getDisplayName();
     method public final java.lang.String getDisplayName(java.util.Locale);
     method public final java.lang.String getDisplayName(android.icu.util.ULocale);
@@ -18267,8 +18338,6 @@
     method public abstract boolean inDaylightTime(java.util.Date);
     method public boolean isFrozen();
     method public boolean observesDaylightTime();
-    method public static synchronized void setDefault(android.icu.util.TimeZone);
-    method public static synchronized void setDefaultTimeZoneType(int);
     method public void setID(java.lang.String);
     method public abstract void setRawOffset(int);
     method public abstract boolean useDaylightTime();
@@ -18281,8 +18350,6 @@
     field public static final int SHORT_COMMONLY_USED = 6; // 0x6
     field public static final int SHORT_GENERIC = 2; // 0x2
     field public static final int SHORT_GMT = 4; // 0x4
-    field public static final int TIMEZONE_ICU = 0; // 0x0
-    field public static final int TIMEZONE_JDK = 1; // 0x1
     field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
     field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
   }
@@ -18378,8 +18445,6 @@
     method public static java.lang.String getVariant(java.lang.String);
     method public boolean isRightToLeft();
     method public static android.icu.util.ULocale minimizeSubtags(android.icu.util.ULocale);
-    method public static synchronized void setDefault(android.icu.util.ULocale);
-    method public static synchronized void setDefault(android.icu.util.ULocale.Category, android.icu.util.ULocale);
     method public android.icu.util.ULocale setKeywordValue(java.lang.String, java.lang.String);
     method public static java.lang.String setKeywordValue(java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String toLanguageTag();
@@ -19338,9 +19403,10 @@
     method public abstract void onAudioFocusChange(int);
   }
 
-  public class AudioRecord {
+  public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
-    method public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+    method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -19364,7 +19430,8 @@
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
-    method public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19402,17 +19469,29 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
+  public abstract interface AudioRouting {
+    method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+    method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
+  }
+
+  public static abstract interface AudioRouting.OnRoutingChangedListener {
+    method public abstract void onRoutingChanged(android.media.AudioRouting);
+  }
+
   public final class AudioTimestamp {
     ctor public AudioTimestamp();
     field public long framePosition;
     field public long nanoTime;
   }
 
-  public class AudioTrack {
+  public class AudioTrack implements android.media.AudioRouting {
     ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
-    method public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+    method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method public void flush();
     method public int getAudioFormat();
@@ -19442,7 +19521,8 @@
     method public void play() throws java.lang.IllegalStateException;
     method public void release();
     method public int reloadStaticData();
-    method public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setAuxEffectSendLevel(float);
     method public int setLoopPoints(int, int, int);
     method public int setNotificationMarkerPosition(int);
@@ -19495,8 +19575,8 @@
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
   }
 
-  public static abstract interface AudioTrack.OnRoutingChangedListener {
-    method public abstract void onRoutingChanged(android.media.AudioTrack);
+  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
+    method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
   }
 
   public class CamcorderProfile {
@@ -20314,8 +20394,6 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22377,6 +22455,24 @@
     ctor public MtpConstants();
     method public static boolean isAbstractObject(int);
     field public static final int ASSOCIATION_TYPE_GENERIC_FOLDER = 1; // 0x1
+    field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
+    field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
+    field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
+    field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
+    field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
+    field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
+    field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
+    field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
+    field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
+    field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
+    field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
+    field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
+    field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
+    field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
+    field public static final int EVENT_STORE_FULL = 16394; // 0x400a
+    field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
+    field public static final int EVENT_UNDEFINED = 16384; // 0x4000
+    field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
     field public static final int FORMAT_3GP_CONTAINER = 47492; // 0xb984
     field public static final int FORMAT_AAC = 47363; // 0xb903
     field public static final int FORMAT_ABSTRACT_AUDIO_ALBUM = 47619; // 0xba03
@@ -22433,6 +22529,41 @@
     field public static final int FORMAT_WMV = 47489; // 0xb981
     field public static final int FORMAT_WPL_PLAYLIST = 47632; // 0xba10
     field public static final int FORMAT_XML_DOCUMENT = 47746; // 0xba82
+    field public static final int OPERATION_CLOSE_SESSION = 4099; // 0x1003
+    field public static final int OPERATION_COPY_OBJECT = 4122; // 0x101a
+    field public static final int OPERATION_DELETE_OBJECT = 4107; // 0x100b
+    field public static final int OPERATION_FORMAT_STORE = 4111; // 0x100f
+    field public static final int OPERATION_GET_DEVICE_INFO = 4097; // 0x1001
+    field public static final int OPERATION_GET_DEVICE_PROP_DESC = 4116; // 0x1014
+    field public static final int OPERATION_GET_DEVICE_PROP_VALUE = 4117; // 0x1015
+    field public static final int OPERATION_GET_NUM_OBJECTS = 4102; // 0x1006
+    field public static final int OPERATION_GET_OBJECT = 4105; // 0x1009
+    field public static final int OPERATION_GET_OBJECT_HANDLES = 4103; // 0x1007
+    field public static final int OPERATION_GET_OBJECT_INFO = 4104; // 0x1008
+    field public static final int OPERATION_GET_OBJECT_PROPS_SUPPORTED = 38913; // 0x9801
+    field public static final int OPERATION_GET_OBJECT_PROP_DESC = 38914; // 0x9802
+    field public static final int OPERATION_GET_OBJECT_PROP_VALUE = 38915; // 0x9803
+    field public static final int OPERATION_GET_OBJECT_REFERENCES = 38928; // 0x9810
+    field public static final int OPERATION_GET_PARTIAL_OBJECT = 4123; // 0x101b
+    field public static final int OPERATION_GET_STORAGE_INFO = 4101; // 0x1005
+    field public static final int OPERATION_GET_STORAGE_I_DS = 4100; // 0x1004
+    field public static final int OPERATION_GET_THUMB = 4106; // 0x100a
+    field public static final int OPERATION_INITIATE_CAPTURE = 4110; // 0x100e
+    field public static final int OPERATION_INITIATE_OPEN_CAPTURE = 4124; // 0x101c
+    field public static final int OPERATION_MOVE_OBJECT = 4121; // 0x1019
+    field public static final int OPERATION_OPEN_SESSION = 4098; // 0x1002
+    field public static final int OPERATION_POWER_DOWN = 4115; // 0x1013
+    field public static final int OPERATION_RESET_DEVICE = 4112; // 0x1010
+    field public static final int OPERATION_RESET_DEVICE_PROP_VALUE = 4119; // 0x1017
+    field public static final int OPERATION_SELF_TEST = 4113; // 0x1011
+    field public static final int OPERATION_SEND_OBJECT = 4109; // 0x100d
+    field public static final int OPERATION_SEND_OBJECT_INFO = 4108; // 0x100c
+    field public static final int OPERATION_SET_DEVICE_PROP_VALUE = 4118; // 0x1016
+    field public static final int OPERATION_SET_OBJECT_PROP_VALUE = 38916; // 0x9804
+    field public static final int OPERATION_SET_OBJECT_PROTECTION = 4114; // 0x1012
+    field public static final int OPERATION_SET_OBJECT_REFERENCES = 38929; // 0x9811
+    field public static final int OPERATION_SKIP = 38944; // 0x9820
+    field public static final int OPERATION_TERMINATE_OPEN_CAPTURE = 4120; // 0x1018
     field public static final int PROTECTION_STATUS_NONE = 0; // 0x0
     field public static final int PROTECTION_STATUS_NON_TRANSFERABLE_DATA = 32771; // 0x8003
     field public static final int PROTECTION_STATUS_READ_ONLY = 32769; // 0x8001
@@ -22450,6 +22581,7 @@
     method public int[] getObjectHandles(int, int, int);
     method public android.mtp.MtpObjectInfo getObjectInfo(int);
     method public long getParent(int);
+    method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
     method public long getStorageId(int);
     method public int[] getStorageIds();
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -22465,31 +22597,23 @@
   public class MtpDeviceInfo {
     method public final java.lang.String getManufacturer();
     method public final java.lang.String getModel();
+    method public final int[] getOperationsSupported();
     method public final java.lang.String getSerialNumber();
     method public final java.lang.String getVersion();
   }
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
-    field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
-    field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
-    field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
-    field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
-    field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
-    field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
-    field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
-    field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
-    field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
-    field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
-    field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
-    field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
-    field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
-    field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
-    field public static final int EVENT_STORE_FULL = 16394; // 0x400a
-    field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
-    field public static final int EVENT_UNDEFINED = 16384; // 0x4000
-    field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -27140,7 +27264,7 @@
     ctor public GLException(int, java.lang.String);
   }
 
-  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
+  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback2 {
     ctor public GLSurfaceView(android.content.Context);
     ctor public GLSurfaceView(android.content.Context, android.util.AttributeSet);
     method public int getDebugFlags();
@@ -27164,6 +27288,7 @@
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -28371,6 +28496,7 @@
   }
 
   public class UserManager {
+    method public static android.content.Intent createUserCreationIntent(java.lang.String, java.lang.String, java.lang.String, android.os.PersistableBundle);
     method public android.os.Bundle getApplicationRestrictions(java.lang.String);
     method public long getSerialNumberForUser(android.os.UserHandle);
     method public int getUserCount();
@@ -28409,6 +28535,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
     field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
     field public static final java.lang.String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
+    field public static final java.lang.String DISALLOW_DATA_ROAMING = "no_data_roaming";
     field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
     field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_FUN = "no_fun";
@@ -29279,6 +29406,22 @@
     field public static final java.lang.String _ID = "_id";
   }
 
+  public class BlockedNumberContract {
+    method public static boolean isBlocked(android.content.Context, java.lang.String);
+    field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
+    field public static final android.net.Uri AUTHORITY_URI;
+  }
+
+  public static class BlockedNumberContract.BlockedNumbers {
+    field public static final java.lang.String COLUMN_E164_NUMBER = "e164_number";
+    field public static final java.lang.String COLUMN_ID = "_id";
+    field public static final java.lang.String COLUMN_ORIGINAL_NUMBER = "original_number";
+    field public static final java.lang.String COLUMN_STRIPPED_NUMBER = "stripped_number";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
   public class Browser {
     ctor public Browser();
     method public static final void sendString(android.content.Context, java.lang.String);
@@ -30776,7 +30919,7 @@
     field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
     field public static final java.lang.String COLUMN_SIZE = "_size";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
-    field public static final int FLAG_ARCHIVE = 2048; // 0x800
+    field public static final int FLAG_ARCHIVE = 1024; // 0x400
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -30785,9 +30928,8 @@
     field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
     field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
-    field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
-    field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
+    field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
     field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
   }
 
@@ -31231,6 +31373,7 @@
     field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
+    field public static final java.lang.String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
     field public static final java.lang.String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
     field public static final java.lang.String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
     field public static final java.lang.String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
@@ -31277,6 +31420,7 @@
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
@@ -33104,6 +33248,7 @@
   public class NetworkSecurityPolicy {
     method public static android.security.NetworkSecurityPolicy getInstance();
     method public boolean isCleartextTrafficPermitted();
+    method public boolean isCleartextTrafficPermitted(java.lang.String);
   }
 
 }
@@ -33398,6 +33543,7 @@
     method public boolean onMenuOpened(int, android.view.Menu);
     method public void onPanelClosed(int, android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public boolean onSearchRequested(android.view.SearchEvent);
     method public boolean onSearchRequested();
     method public void onWakeUp();
@@ -33492,24 +33638,18 @@
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+    field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
     field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  public class NotificationAdjustment implements android.os.Parcelable {
-    ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
-  }
-
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
     method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33525,10 +33665,15 @@
     field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
+  public class NotificationAssistantService.Adjustment {
+    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+  }
+
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
@@ -33551,6 +33696,8 @@
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
+    method public static final void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
+    method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
     field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
@@ -33562,6 +33709,7 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
     field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
+    field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4
   }
 
   public static class NotificationListenerService.Ranking {
@@ -33618,25 +33766,37 @@
     method public java.lang.CharSequence getContentDescription();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
+    method public int getState();
     method public void setContentDescription(java.lang.CharSequence);
     method public void setIcon(android.graphics.drawable.Icon);
     method public void setLabel(java.lang.CharSequence);
+    method public void setState(int);
     method public void updateTile();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+    field public static final int STATE_ACTIVE = 2; // 0x2
+    field public static final int STATE_INACTIVE = 1; // 0x1
+    field public static final int STATE_UNAVAILABLE = 0; // 0x0
   }
 
   public class TileService extends android.app.Service {
     ctor public TileService();
     method public final android.service.quicksettings.Tile getQsTile();
+    method public final boolean isLocked();
+    method public final boolean isSecure();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public void onTileAdded();
+    method public int onTileAdded();
     method public void onTileRemoved();
+    method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void showDialog(android.app.Dialog);
+    method public final void startActivityAndCollapse(android.content.Intent);
+    method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
+    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
+    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
   }
 
 }
@@ -34897,6 +35057,7 @@
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_WIFI = 8; // 0x8
+    field public static final int PROPERTY_WORK_CALL = 32; // 0x20
   }
 
   public final class CallAudioState implements android.os.Parcelable {
@@ -36041,6 +36202,7 @@
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
+    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method public boolean iccCloseLogicalChannel(int);
@@ -36053,6 +36215,7 @@
     method public boolean isSmsCapable();
     method public boolean isTtyModeSupported();
     method public boolean isVoiceCapable();
+    method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
@@ -36735,10 +36898,7 @@
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
-    method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public byte[] getEphemeralCookie();
-    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36747,8 +36907,10 @@
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInstaller getPackageInstaller();
+    method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] getPackagesForUid(int);
     method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -36769,7 +36931,6 @@
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
-    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36787,8 +36948,6 @@
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public boolean setDefaultBrowserPackageName(java.lang.String, int);
-    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
@@ -37991,9 +38150,11 @@
 
   public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public LocaleSpan(java.util.Locale);
+    ctor public LocaleSpan(android.util.LocaleList);
     ctor public LocaleSpan(android.os.Parcel);
     method public int describeContents();
     method public java.util.Locale getLocale();
+    method public android.util.LocaleList getLocales();
     method public int getSpanTypeId();
     method public void updateDrawState(android.text.TextPaint);
     method public void updateMeasureState(android.text.TextPaint);
@@ -38099,7 +38260,8 @@
     ctor public SuggestionSpan(android.os.Parcel);
     method public int describeContents();
     method public int getFlags();
-    method public java.lang.String getLocale();
+    method public deprecated java.lang.String getLocale();
+    method public java.util.Locale getLocaleObject();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
     method public void setFlags(int);
@@ -39019,7 +39181,9 @@
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
     method public java.util.Locale getPrimary();
+    method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
+    method public static void setDefault(android.util.LocaleList);
     method public int size();
     method public java.lang.String toLanguageTags();
     method public void writeToParcel(android.os.Parcel, int);
@@ -39177,7 +39341,7 @@
     method public static final java.lang.String digitsAndPlusOnly(java.util.regex.Matcher);
     field public static final java.util.regex.Pattern DOMAIN_NAME;
     field public static final java.util.regex.Pattern EMAIL_ADDRESS;
-    field public static final java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
+    field public static final deprecated java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
     field public static final java.util.regex.Pattern IP_ADDRESS;
     field public static final java.util.regex.Pattern PHONE;
     field public static final deprecated java.util.regex.Pattern TOP_LEVEL_DOMAIN;
@@ -40281,6 +40445,27 @@
     method public void startTracking(android.view.KeyEvent, java.lang.Object);
   }
 
+  public final class KeyboardShortcutGroup implements android.os.Parcelable {
+    ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>);
+    ctor public KeyboardShortcutGroup(java.lang.CharSequence);
+    method public void addItem(android.view.KeyboardShortcutInfo);
+    method public int describeContents();
+    method public java.util.List<android.view.KeyboardShortcutInfo> getItems();
+    method public java.lang.CharSequence getLabel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR;
+  }
+
+  public final class KeyboardShortcutInfo implements android.os.Parcelable {
+    ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
+    method public int describeContents();
+    method public char getBaseCharacter();
+    method public java.lang.CharSequence getLabel();
+    method public int getModifiers();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR;
+  }
+
   public abstract class LayoutInflater {
     ctor protected LayoutInflater(android.content.Context);
     ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
@@ -40556,7 +40741,6 @@
     field public static final int AXIS_RX = 12; // 0xc
     field public static final int AXIS_RY = 13; // 0xd
     field public static final int AXIS_RZ = 14; // 0xe
-    field public static final int AXIS_SCROLL = 26; // 0x1a
     field public static final int AXIS_SIZE = 3; // 0x3
     field public static final int AXIS_THROTTLE = 19; // 0x13
     field public static final int AXIS_TILT = 25; // 0x19
@@ -42283,6 +42467,7 @@
     method public abstract boolean onMenuOpened(int, android.view.Menu);
     method public abstract void onPanelClosed(int, android.view.Menu);
     method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public abstract boolean onSearchRequested();
     method public abstract boolean onSearchRequested(android.view.SearchEvent);
     method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -43483,6 +43668,7 @@
   }
 
   public final class InputMethodManager {
+    method public void dispatchKeyEventFromInputMethod(android.view.View, android.view.KeyEvent);
     method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
     method public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
     method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
@@ -44043,7 +44229,7 @@
     method public abstract deprecated void setEnableSmoothTransition(boolean);
     method public abstract void setFantasyFontFamily(java.lang.String);
     method public abstract void setFixedFontFamily(java.lang.String);
-    method public abstract void setGeolocationDatabasePath(java.lang.String);
+    method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
     method public abstract void setGeolocationEnabled(boolean);
     method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
     method public abstract void setJavaScriptEnabled(boolean);
@@ -44485,12 +44671,18 @@
     method public int getThumbOffset();
     method public android.content.res.ColorStateList getThumbTintList();
     method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTickMark();
+    method public android.content.res.ColorStateList getTickMarkTintList();
+    method public android.graphics.PorterDuff.Mode getTickMarkTintMode();
     method public void setKeyProgressIncrement(int);
     method public void setSplitTrack(boolean);
     method public void setThumb(android.graphics.drawable.Drawable);
     method public void setThumbOffset(int);
     method public void setThumbTintList(android.content.res.ColorStateList);
     method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTickMark(android.graphics.drawable.Drawable);
+    method public void setTickMarkTintList(android.content.res.ColorStateList);
+    method public void setTickMarkTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public abstract class AbsSpinner extends android.widget.AdapterView {
@@ -49438,6 +49630,8 @@
   public class InternalError extends java.lang.VirtualMachineError {
     ctor public InternalError();
     ctor public InternalError(java.lang.String);
+    ctor public InternalError(java.lang.String, java.lang.Throwable);
+    ctor public InternalError(java.lang.Throwable);
   }
 
   public class InterruptedException extends java.lang.Exception {
@@ -50210,6 +50404,8 @@
   public abstract class VirtualMachineError extends java.lang.Error {
     ctor public VirtualMachineError();
     ctor public VirtualMachineError(java.lang.String);
+    ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+    ctor public VirtualMachineError(java.lang.Throwable);
   }
 
   public final class Void {
@@ -53952,6 +54148,7 @@
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
   }
 
   public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -53969,6 +54166,13 @@
   public abstract class CertPathBuilderSpi {
     ctor public CertPathBuilderSpi();
     method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public abstract interface CertPathChecker {
+    method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public abstract boolean isForwardCheckingSupported();
   }
 
   public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -53983,6 +54187,7 @@
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
     method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54019,6 +54224,7 @@
 
   public abstract class CertPathValidatorSpi {
     ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
     method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54177,9 +54383,10 @@
     method public java.security.cert.CertPath getCertPath();
   }
 
-  public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
     ctor protected PKIXCertPathChecker();
     method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
     method public java.lang.Object clone();
     method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
     method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -54239,6 +54446,30 @@
     enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
   }
 
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+    method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+    method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
   public abstract interface PolicyNode {
     method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
     method public abstract int getDepth();
@@ -54398,6 +54629,7 @@
     method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
     method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
     method public abstract int getVersion();
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract interface X509Extension {
diff --git a/api/removed.txt b/api/removed.txt
index f12e61e..6b7961e 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -199,6 +199,15 @@
 
 }
 
+package android.test.mock {
+
+  public class MockPackageManager extends android.content.pm.PackageManager {
+    method public deprecated java.lang.String getDefaultBrowserPackageName(int);
+    method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
+  }
+
+}
+
 package android.text.format {
 
   public class DateFormat {
diff --git a/api/system-current.txt b/api/system-current.txt
index 4db2123..12b9b32 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -93,7 +93,6 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
-    field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT";
     field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final java.lang.String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
@@ -215,6 +214,7 @@
     field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
     field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final java.lang.String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
+    field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
     field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
@@ -429,6 +429,7 @@
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844040; // 0x1010508
+    field public static final int canPerformGestures = 16844046; // 0x101050e
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
     field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -618,6 +619,7 @@
     field public static final int expandableListViewStyle = 16842863; // 0x101006f
     field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
     field public static final int exported = 16842768; // 0x1010010
+    field public static final int externalService = 16844047; // 0x101050f
     field public static final int extraTension = 16843371; // 0x101026b
     field public static final int extractNativeLibs = 16844010; // 0x10104ea
     field public static final int factor = 16843219; // 0x10101d3
@@ -1387,6 +1389,9 @@
     field public static final int thumbTint = 16843889; // 0x1010471
     field public static final int thumbTintMode = 16843890; // 0x1010472
     field public static final int thumbnail = 16843429; // 0x10102a5
+    field public static final int tickMark = 16844043; // 0x101050b
+    field public static final int tickMarkTint = 16844044; // 0x101050c
+    field public static final int tickMarkTintMode = 16844045; // 0x101050d
     field public static final int tileMode = 16843265; // 0x1010201
     field public static final int tileModeX = 16843895; // 0x1010477
     field public static final int tileModeY = 16843896; // 0x1010478
@@ -2649,6 +2654,7 @@
     field public static final int Widget_Material_ScrollView = 16974462; // 0x103027e
     field public static final int Widget_Material_SearchView = 16974463; // 0x103027f
     field public static final int Widget_Material_SeekBar = 16974464; // 0x1030280
+    field public static final int Widget_Material_SeekBar_Discrete = 16974553; // 0x10302d9
     field public static final int Widget_Material_SegmentedButton = 16974465; // 0x1030281
     field public static final int Widget_Material_Spinner = 16974467; // 0x1030283
     field public static final int Widget_Material_Spinner_Underlined = 16974468; // 0x1030284
@@ -2708,6 +2714,7 @@
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -2747,6 +2754,12 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
 
+  public static abstract class AccessibilityService.GestureResultCallback {
+    ctor public AccessibilityService.GestureResultCallback();
+    method public void onCancelled(android.accessibilityservice.GestureDescription);
+    method public void onCompleted(android.accessibilityservice.GestureDescription);
+  }
+
   public static final class AccessibilityService.MagnificationController {
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
@@ -2779,6 +2792,7 @@
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
+    field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
     field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2805,6 +2819,30 @@
     field public java.lang.String[] packageNames;
   }
 
+  public final class GestureDescription {
+    method public static android.accessibilityservice.GestureDescription createClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
+    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
+    method public int getStrokeCount();
+    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
+    field public static final int MAX_STROKE_COUNT = 10; // 0xa
+  }
+
+  public static class GestureDescription.Builder {
+    ctor public GestureDescription.Builder();
+    method public android.accessibilityservice.GestureDescription.Builder addStroke(android.accessibilityservice.GestureDescription.StrokeDescription);
+    method public android.accessibilityservice.GestureDescription build();
+  }
+
+  public static class GestureDescription.StrokeDescription {
+    ctor public GestureDescription.StrokeDescription(android.graphics.Path, long, long);
+    method public long getDuration();
+    method public android.graphics.Path getPath();
+    method public long getStartTime();
+  }
+
 }
 
 package android.accounts {
@@ -3574,6 +3612,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
@@ -3750,6 +3789,9 @@
     method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
   }
 
+  public static abstract class ActivityManager.BugreportMode implements java.lang.annotation.Annotation {
+  }
+
   public static class ActivityManager.MemoryInfo implements android.os.Parcelable {
     ctor public ActivityManager.MemoryInfo();
     method public int describeContents();
@@ -4270,6 +4312,7 @@
     method public void onPanelClosed(int, android.view.Menu);
     method public boolean onPrepareOptionsMenu(android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public void onRestoreInstanceState(android.os.Bundle);
     method public android.os.Bundle onSaveInstanceState();
     method public boolean onSearchRequested(android.view.SearchEvent);
@@ -4349,7 +4392,7 @@
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_ID = "_id";
     field public static final java.lang.String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
-    field public static final java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
+    field public static final deprecated java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
     field public static final java.lang.String COLUMN_LOCAL_URI = "local_uri";
     field public static final java.lang.String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
     field public static final java.lang.String COLUMN_MEDIA_TYPE = "media_type";
@@ -5318,6 +5361,7 @@
     field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff
     field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
+    field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4
     field public final int priorityCallSenders;
     field public final int priorityCategories;
     field public final int priorityMessageSenders;
@@ -5871,6 +5915,7 @@
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
+    method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
@@ -5878,6 +5923,7 @@
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public deprecated java.lang.String getDeviceInitializerApp();
@@ -5886,9 +5932,11 @@
     method public java.lang.String getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
+    method public java.lang.String getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
+    method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
     method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -5910,6 +5958,7 @@
     method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
     method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
+    method public java.lang.String getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5940,6 +5989,7 @@
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
+    method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
@@ -5948,11 +5998,13 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
     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);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
+    method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5977,6 +6029,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
@@ -5992,6 +6045,7 @@
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
+    field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
@@ -6335,6 +6389,7 @@
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.PersistableBundle getExtras();
+    method public long getFlexMillis();
     method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
@@ -6352,6 +6407,8 @@
     field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
     field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6365,6 +6422,7 @@
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
+    method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -6425,6 +6483,8 @@
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
+    method public int getMetering();
+    method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
     method public long getStartTimeStamp();
@@ -6432,6 +6492,12 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int METERING_ALL = -1; // 0xffffffff
+    field public static final int METERING_DEFAULT = 1; // 0x1
+    field public static final int METERING_METERED = 2; // 0x2
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
+    field public static final int ROAMING_DEFAULT = 1; // 0x1
+    field public static final int ROAMING_ROAMING = 2; // 0x2
     field public static final int STATE_ALL = -1; // 0xffffffff
     field public static final int STATE_DEFAULT = 1; // 0x1
     field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -7367,6 +7433,15 @@
     field public static final int TYPE_SCO = 2; // 0x2
   }
 
+  public class OobData implements android.os.Parcelable {
+    ctor public OobData();
+    method public int describeContents();
+    method public byte[] getSecurityManagerTk();
+    method public void setSecurityManagerTk(byte[]);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+  }
+
 }
 
 package android.bluetooth.le {
@@ -7811,11 +7886,12 @@
     method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
   }
 
-  public class ContentProviderClient {
+  public class ContentProviderClient implements java.lang.AutoCloseable {
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
     method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+    method public void close();
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
     method public android.content.ContentProvider getLocalContentProvider();
     method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -7829,7 +7905,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
-    method public boolean release();
+    method public deprecated boolean release();
     method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
   }
@@ -8143,6 +8219,7 @@
     field public static final int BIND_ALLOW_OOM_MANAGEMENT = 16; // 0x10
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
+    field public static final int BIND_EXTERNAL_SERVICE = -2147483648; // 0x80000000
     field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
     field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
@@ -9550,12 +9627,6 @@
     method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
   }
 
-  public class ManifestDigest implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.content.pm.ManifestDigest> CREATOR;
-  }
-
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
@@ -9737,8 +9808,6 @@
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract byte[] getEphemeralCookie();
-    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9748,8 +9817,10 @@
     method public abstract java.lang.String getNameForUid(int);
     method public android.content.pm.PackageInfo getPackageArchiveInfo(java.lang.String, int);
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
+    method public abstract int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] getPackagesForUid(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
@@ -9772,7 +9843,6 @@
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
-    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9792,7 +9862,6 @@
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -9829,6 +9898,7 @@
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+    field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
@@ -9887,8 +9957,8 @@
     field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
     field public static final int GET_ACTIVITIES = 1; // 0x1
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
-    field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
-    field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+    field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
+    field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
     field public static final int GET_GIDS = 256; // 0x100
     field public static final int GET_INSTRUMENTATION = 16; // 0x10
     field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -9900,7 +9970,7 @@
     field public static final int GET_SERVICES = 4; // 0x4
     field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
     field public static final int GET_SIGNATURES = 64; // 0x40
-    field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
+    field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
     field public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; // 0xffffffff
     field public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; // 0xfffffff3
@@ -9941,9 +10011,13 @@
     field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+    field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
+    field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+    field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000
     field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000
-    field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000
-    field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000
+    field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000
+    field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
+    field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -11200,7 +11274,7 @@
     field public final int statusCode;
   }
 
-  public class DrmManagerClient {
+  public class DrmManagerClient implements java.lang.AutoCloseable {
     ctor public DrmManagerClient(android.content.Context);
     method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
     method public int acquireRights(android.drm.DrmInfoRequest);
@@ -11210,6 +11284,7 @@
     method public int checkRightsStatus(android.net.Uri);
     method public int checkRightsStatus(java.lang.String, int);
     method public int checkRightsStatus(android.net.Uri, int);
+    method public void close();
     method public android.drm.DrmConvertedStatus closeConvertSession(int);
     method public android.drm.DrmConvertedStatus convertData(int, byte[]);
     method public java.lang.String[] getAvailableDrmEngines();
@@ -11223,7 +11298,7 @@
     method public java.lang.String getOriginalMimeType(android.net.Uri);
     method public int openConvertSession(java.lang.String);
     method public int processDrmInfo(android.drm.DrmInfo);
-    method public void release();
+    method public deprecated void release();
     method public int removeAllRights();
     method public int removeRights(java.lang.String);
     method public int removeRights(android.net.Uri);
@@ -11627,14 +11702,14 @@
 
   public static class BitmapFactory.Options {
     ctor public BitmapFactory.Options();
-    method public void requestCancelDecode();
+    method public deprecated void requestCancelDecode();
     field public android.graphics.Bitmap inBitmap;
     field public int inDensity;
-    field public boolean inDither;
+    field public deprecated boolean inDither;
     field public deprecated boolean inInputShareable;
     field public boolean inJustDecodeBounds;
     field public boolean inMutable;
-    field public boolean inPreferQualityOverSpeed;
+    field public deprecated boolean inPreferQualityOverSpeed;
     field public android.graphics.Bitmap.Config inPreferredConfig;
     field public boolean inPremultiplied;
     field public deprecated boolean inPurgeable;
@@ -11643,7 +11718,7 @@
     field public int inScreenDensity;
     field public int inTargetDensity;
     field public byte[] inTempStorage;
-    field public boolean mCancel;
+    field public deprecated boolean mCancel;
     field public int outHeight;
     field public java.lang.String outMimeType;
     field public int outWidth;
@@ -13082,6 +13157,7 @@
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
+    field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
   }
 
   public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -19230,7 +19306,6 @@
     method public static java.lang.String getCanonicalID(java.lang.String, boolean[]);
     method public int getDSTSavings();
     method public static android.icu.util.TimeZone getDefault();
-    method public static int getDefaultTimeZoneType();
     method public final java.lang.String getDisplayName();
     method public final java.lang.String getDisplayName(java.util.Locale);
     method public final java.lang.String getDisplayName(android.icu.util.ULocale);
@@ -19254,8 +19329,6 @@
     method public abstract boolean inDaylightTime(java.util.Date);
     method public boolean isFrozen();
     method public boolean observesDaylightTime();
-    method public static synchronized void setDefault(android.icu.util.TimeZone);
-    method public static synchronized void setDefaultTimeZoneType(int);
     method public void setID(java.lang.String);
     method public abstract void setRawOffset(int);
     method public abstract boolean useDaylightTime();
@@ -19268,8 +19341,6 @@
     field public static final int SHORT_COMMONLY_USED = 6; // 0x6
     field public static final int SHORT_GENERIC = 2; // 0x2
     field public static final int SHORT_GMT = 4; // 0x4
-    field public static final int TIMEZONE_ICU = 0; // 0x0
-    field public static final int TIMEZONE_JDK = 1; // 0x1
     field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
     field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
   }
@@ -19365,8 +19436,6 @@
     method public static java.lang.String getVariant(java.lang.String);
     method public boolean isRightToLeft();
     method public static android.icu.util.ULocale minimizeSubtags(android.icu.util.ULocale);
-    method public static synchronized void setDefault(android.icu.util.ULocale);
-    method public static synchronized void setDefault(android.icu.util.ULocale.Category, android.icu.util.ULocale);
     method public android.icu.util.ULocale setKeywordValue(java.lang.String, java.lang.String);
     method public static java.lang.String setKeywordValue(java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String toLanguageTag();
@@ -20639,10 +20708,11 @@
     method public abstract void onAudioFocusChange(int);
   }
 
-  public class AudioRecord {
+  public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
-    method public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+    method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -20666,7 +20736,8 @@
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
-    method public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -20706,17 +20777,29 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
+  public abstract interface AudioRouting {
+    method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+    method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
+  }
+
+  public static abstract interface AudioRouting.OnRoutingChangedListener {
+    method public abstract void onRoutingChanged(android.media.AudioRouting);
+  }
+
   public final class AudioTimestamp {
     ctor public AudioTimestamp();
     field public long framePosition;
     field public long nanoTime;
   }
 
-  public class AudioTrack {
+  public class AudioTrack implements android.media.AudioRouting {
     ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
-    method public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+    method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method public void flush();
     method public int getAudioFormat();
@@ -20746,7 +20829,8 @@
     method public void play() throws java.lang.IllegalStateException;
     method public void release();
     method public int reloadStaticData();
-    method public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setAuxEffectSendLevel(float);
     method public int setLoopPoints(int, int, int);
     method public int setNotificationMarkerPosition(int);
@@ -20799,8 +20883,8 @@
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
   }
 
-  public static abstract interface AudioTrack.OnRoutingChangedListener {
-    method public abstract void onRoutingChanged(android.media.AudioTrack);
+  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
+    method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
   }
 
   public class CamcorderProfile {
@@ -21618,8 +21702,6 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -23929,6 +24011,24 @@
     ctor public MtpConstants();
     method public static boolean isAbstractObject(int);
     field public static final int ASSOCIATION_TYPE_GENERIC_FOLDER = 1; // 0x1
+    field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
+    field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
+    field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
+    field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
+    field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
+    field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
+    field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
+    field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
+    field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
+    field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
+    field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
+    field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
+    field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
+    field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
+    field public static final int EVENT_STORE_FULL = 16394; // 0x400a
+    field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
+    field public static final int EVENT_UNDEFINED = 16384; // 0x4000
+    field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
     field public static final int FORMAT_3GP_CONTAINER = 47492; // 0xb984
     field public static final int FORMAT_AAC = 47363; // 0xb903
     field public static final int FORMAT_ABSTRACT_AUDIO_ALBUM = 47619; // 0xba03
@@ -23985,6 +24085,41 @@
     field public static final int FORMAT_WMV = 47489; // 0xb981
     field public static final int FORMAT_WPL_PLAYLIST = 47632; // 0xba10
     field public static final int FORMAT_XML_DOCUMENT = 47746; // 0xba82
+    field public static final int OPERATION_CLOSE_SESSION = 4099; // 0x1003
+    field public static final int OPERATION_COPY_OBJECT = 4122; // 0x101a
+    field public static final int OPERATION_DELETE_OBJECT = 4107; // 0x100b
+    field public static final int OPERATION_FORMAT_STORE = 4111; // 0x100f
+    field public static final int OPERATION_GET_DEVICE_INFO = 4097; // 0x1001
+    field public static final int OPERATION_GET_DEVICE_PROP_DESC = 4116; // 0x1014
+    field public static final int OPERATION_GET_DEVICE_PROP_VALUE = 4117; // 0x1015
+    field public static final int OPERATION_GET_NUM_OBJECTS = 4102; // 0x1006
+    field public static final int OPERATION_GET_OBJECT = 4105; // 0x1009
+    field public static final int OPERATION_GET_OBJECT_HANDLES = 4103; // 0x1007
+    field public static final int OPERATION_GET_OBJECT_INFO = 4104; // 0x1008
+    field public static final int OPERATION_GET_OBJECT_PROPS_SUPPORTED = 38913; // 0x9801
+    field public static final int OPERATION_GET_OBJECT_PROP_DESC = 38914; // 0x9802
+    field public static final int OPERATION_GET_OBJECT_PROP_VALUE = 38915; // 0x9803
+    field public static final int OPERATION_GET_OBJECT_REFERENCES = 38928; // 0x9810
+    field public static final int OPERATION_GET_PARTIAL_OBJECT = 4123; // 0x101b
+    field public static final int OPERATION_GET_STORAGE_INFO = 4101; // 0x1005
+    field public static final int OPERATION_GET_STORAGE_I_DS = 4100; // 0x1004
+    field public static final int OPERATION_GET_THUMB = 4106; // 0x100a
+    field public static final int OPERATION_INITIATE_CAPTURE = 4110; // 0x100e
+    field public static final int OPERATION_INITIATE_OPEN_CAPTURE = 4124; // 0x101c
+    field public static final int OPERATION_MOVE_OBJECT = 4121; // 0x1019
+    field public static final int OPERATION_OPEN_SESSION = 4098; // 0x1002
+    field public static final int OPERATION_POWER_DOWN = 4115; // 0x1013
+    field public static final int OPERATION_RESET_DEVICE = 4112; // 0x1010
+    field public static final int OPERATION_RESET_DEVICE_PROP_VALUE = 4119; // 0x1017
+    field public static final int OPERATION_SELF_TEST = 4113; // 0x1011
+    field public static final int OPERATION_SEND_OBJECT = 4109; // 0x100d
+    field public static final int OPERATION_SEND_OBJECT_INFO = 4108; // 0x100c
+    field public static final int OPERATION_SET_DEVICE_PROP_VALUE = 4118; // 0x1016
+    field public static final int OPERATION_SET_OBJECT_PROP_VALUE = 38916; // 0x9804
+    field public static final int OPERATION_SET_OBJECT_PROTECTION = 4114; // 0x1012
+    field public static final int OPERATION_SET_OBJECT_REFERENCES = 38929; // 0x9811
+    field public static final int OPERATION_SKIP = 38944; // 0x9820
+    field public static final int OPERATION_TERMINATE_OPEN_CAPTURE = 4120; // 0x1018
     field public static final int PROTECTION_STATUS_NONE = 0; // 0x0
     field public static final int PROTECTION_STATUS_NON_TRANSFERABLE_DATA = 32771; // 0x8003
     field public static final int PROTECTION_STATUS_READ_ONLY = 32769; // 0x8001
@@ -24002,6 +24137,7 @@
     method public int[] getObjectHandles(int, int, int);
     method public android.mtp.MtpObjectInfo getObjectInfo(int);
     method public long getParent(int);
+    method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
     method public long getStorageId(int);
     method public int[] getStorageIds();
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -24017,31 +24153,23 @@
   public class MtpDeviceInfo {
     method public final java.lang.String getManufacturer();
     method public final java.lang.String getModel();
+    method public final int[] getOperationsSupported();
     method public final java.lang.String getSerialNumber();
     method public final java.lang.String getVersion();
   }
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
-    field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
-    field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
-    field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
-    field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
-    field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
-    field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
-    field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
-    field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
-    field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
-    field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
-    field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
-    field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
-    field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
-    field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
-    field public static final int EVENT_STORE_FULL = 16394; // 0x400a
-    field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
-    field public static final int EVENT_UNDEFINED = 16384; // 0x4000
-    field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -24122,6 +24250,7 @@
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
+    method public java.lang.String getCaptivePortalServerUrl();
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
@@ -29136,7 +29265,7 @@
     ctor public GLException(int, java.lang.String);
   }
 
-  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
+  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback2 {
     ctor public GLSurfaceView(android.content.Context);
     ctor public GLSurfaceView(android.content.Context, android.util.AttributeSet);
     method public int getDebugFlags();
@@ -29160,6 +29289,7 @@
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -30379,7 +30509,12 @@
   }
 
   public class UserManager {
+    method public void clearSeedAccountData();
+    method public static android.content.Intent createUserCreationIntent(java.lang.String, java.lang.String, java.lang.String, android.os.PersistableBundle);
     method public android.os.Bundle getApplicationRestrictions(java.lang.String);
+    method public java.lang.String getSeedAccountName();
+    method public android.os.PersistableBundle getSeedAccountOptions();
+    method public java.lang.String getSeedAccountType();
     method public long getSerialNumberForUser(android.os.UserHandle);
     method public int getUserCount();
     method public long getUserCreationTime(android.os.UserHandle);
@@ -30418,6 +30553,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
     field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
     field public static final java.lang.String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
+    field public static final java.lang.String DISALLOW_DATA_ROAMING = "no_data_roaming";
     field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
     field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_FUN = "no_fun";
@@ -31288,6 +31424,22 @@
     field public static final java.lang.String _ID = "_id";
   }
 
+  public class BlockedNumberContract {
+    method public static boolean isBlocked(android.content.Context, java.lang.String);
+    field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
+    field public static final android.net.Uri AUTHORITY_URI;
+  }
+
+  public static class BlockedNumberContract.BlockedNumbers {
+    field public static final java.lang.String COLUMN_E164_NUMBER = "e164_number";
+    field public static final java.lang.String COLUMN_ID = "_id";
+    field public static final java.lang.String COLUMN_ORIGINAL_NUMBER = "original_number";
+    field public static final java.lang.String COLUMN_STRIPPED_NUMBER = "stripped_number";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
   public class Browser {
     ctor public Browser();
     method public static final void sendString(android.content.Context, java.lang.String);
@@ -32815,7 +32967,7 @@
     field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
     field public static final java.lang.String COLUMN_SIZE = "_size";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
-    field public static final int FLAG_ARCHIVE = 2048; // 0x800
+    field public static final int FLAG_ARCHIVE = 1024; // 0x400
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -32824,9 +32976,8 @@
     field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
     field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
-    field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
-    field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
+    field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
     field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
   }
 
@@ -33372,6 +33523,7 @@
     field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
+    field public static final java.lang.String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
     field public static final java.lang.String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
     field public static final java.lang.String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
     field public static final java.lang.String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
@@ -33418,6 +33570,7 @@
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
@@ -35246,6 +35399,7 @@
   public class NetworkSecurityPolicy {
     method public static android.security.NetworkSecurityPolicy getInstance();
     method public boolean isCleartextTrafficPermitted();
+    method public boolean isCleartextTrafficPermitted(java.lang.String);
   }
 
 }
@@ -35540,6 +35694,7 @@
     method public boolean onMenuOpened(int, android.view.Menu);
     method public void onPanelClosed(int, android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public boolean onSearchRequested(android.view.SearchEvent);
     method public boolean onSearchRequested();
     method public void onWakeUp();
@@ -35634,24 +35789,18 @@
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+    field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
     field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  public class NotificationAdjustment implements android.os.Parcelable {
-    ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
-  }
-
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
     method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -35667,10 +35816,15 @@
     field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
+  public class NotificationAssistantService.Adjustment {
+    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+  }
+
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
@@ -35696,6 +35850,8 @@
     method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
+    method public static final void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
+    method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void setOnNotificationPostedTrim(int);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
@@ -35709,6 +35865,7 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
     field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
+    field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4
     field public static final int TRIM_FULL = 0; // 0x0
     field public static final int TRIM_LIGHT = 1; // 0x1
   }
@@ -35792,25 +35949,38 @@
     method public java.lang.CharSequence getContentDescription();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
+    method public int getState();
     method public void setContentDescription(java.lang.CharSequence);
     method public void setIcon(android.graphics.drawable.Icon);
     method public void setLabel(java.lang.CharSequence);
+    method public void setState(int);
     method public void updateTile();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+    field public static final int STATE_ACTIVE = 2; // 0x2
+    field public static final int STATE_INACTIVE = 1; // 0x1
+    field public static final int STATE_UNAVAILABLE = 0; // 0x0
   }
 
   public class TileService extends android.app.Service {
     ctor public TileService();
     method public final android.service.quicksettings.Tile getQsTile();
+    method public final boolean isLocked();
+    method public final boolean isSecure();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public void onTileAdded();
+    method public int onTileAdded();
     method public void onTileRemoved();
+    method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
+    method public final void setStatusIcon(android.graphics.drawable.Icon, java.lang.String);
     method public final void showDialog(android.app.Dialog);
+    method public final void startActivityAndCollapse(android.content.Intent);
+    method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
+    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
+    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
   }
 
 }
@@ -37114,6 +37284,7 @@
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_WIFI = 8; // 0x8
+    field public static final int PROPERTY_WORK_CALL = 32; // 0x20
   }
 
   public static abstract deprecated class Call.Listener extends android.telecom.Call.Callback {
@@ -37417,6 +37588,35 @@
     method public abstract void onVideoQualityChanged(int);
   }
 
+  public class ParcelableCallAnalytics implements android.os.Parcelable {
+    ctor public ParcelableCallAnalytics(long, long, int, boolean, boolean, int, int, boolean, java.lang.String, boolean);
+    ctor public ParcelableCallAnalytics(android.os.Parcel);
+    method public int describeContents();
+    method public long getCallDurationMillis();
+    method public int getCallTechnologies();
+    method public int getCallTerminationCode();
+    method public int getCallType();
+    method public java.lang.String getConnectionService();
+    method public long getStartTimeMillis();
+    method public boolean isAdditionalCall();
+    method public boolean isCreatedFromExistingConnection();
+    method public boolean isEmergencyCall();
+    method public boolean isInterrupted();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CALLTYPE_INCOMING = 1; // 0x1
+    field public static final int CALLTYPE_OUTGOING = 2; // 0x2
+    field public static final int CALLTYPE_UNKNOWN = 0; // 0x0
+    field public static final int CDMA_PHONE = 1; // 0x1
+    field public static final android.os.Parcelable.Creator<android.telecom.ParcelableCallAnalytics> CREATOR;
+    field public static final int GSM_PHONE = 2; // 0x2
+    field public static final int IMS_PHONE = 4; // 0x4
+    field public static final long MILLIS_IN_1_SECOND = 1000L; // 0x3e8L
+    field public static final long MILLIS_IN_5_MINUTES = 300000L; // 0x493e0L
+    field public static final int SIP_PHONE = 8; // 0x8
+    field public static final int STILL_CONNECTED = -1; // 0xffffffff
+    field public static final int THIRD_PARTY_PHONE = 16; // 0x10
+  }
+
   public final deprecated class Phone {
     method public final void addListener(android.telecom.Phone.Listener);
     method public final boolean canAddCall();
@@ -37634,6 +37834,7 @@
     method public void cancelMissedCallsNotification();
     method public deprecated void clearAccounts();
     method public void clearPhoneAccounts();
+    method public java.util.List<android.telecom.ParcelableCallAnalytics> dumpAnalytics();
     method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
     method public boolean endCall();
     method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -37818,6 +38019,7 @@
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
     field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
     field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+    field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
     field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
@@ -38348,6 +38550,7 @@
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
+    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
     method public boolean hasCarrierPrivileges();
@@ -38364,11 +38567,11 @@
     method public boolean isOffhook();
     method public boolean isRadioOn();
     method public boolean isRinging();
-    method public boolean isSimPinEnabled();
     method public boolean isSmsCapable();
     method public boolean isTtyModeSupported();
     method public boolean isVideoCallingEnabled();
     method public boolean isVoiceCapable();
+    method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public boolean needsOtaServiceProvisioning();
@@ -39082,10 +39285,7 @@
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
-    method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public byte[] getEphemeralCookie();
-    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -39094,8 +39294,10 @@
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInstaller getPackageInstaller();
+    method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] getPackagesForUid(int);
     method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
@@ -39118,7 +39320,6 @@
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
-    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -39138,8 +39339,6 @@
     method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public boolean setDefaultBrowserPackageName(java.lang.String, int);
-    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -40344,9 +40543,11 @@
 
   public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public LocaleSpan(java.util.Locale);
+    ctor public LocaleSpan(android.util.LocaleList);
     ctor public LocaleSpan(android.os.Parcel);
     method public int describeContents();
     method public java.util.Locale getLocale();
+    method public android.util.LocaleList getLocales();
     method public int getSpanTypeId();
     method public void updateDrawState(android.text.TextPaint);
     method public void updateMeasureState(android.text.TextPaint);
@@ -40452,7 +40653,8 @@
     ctor public SuggestionSpan(android.os.Parcel);
     method public int describeContents();
     method public int getFlags();
-    method public java.lang.String getLocale();
+    method public deprecated java.lang.String getLocale();
+    method public java.util.Locale getLocaleObject();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
     method public void setFlags(int);
@@ -41372,7 +41574,9 @@
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
     method public java.util.Locale getPrimary();
+    method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
+    method public static void setDefault(android.util.LocaleList);
     method public int size();
     method public java.lang.String toLanguageTags();
     method public void writeToParcel(android.os.Parcel, int);
@@ -41530,7 +41734,7 @@
     method public static final java.lang.String digitsAndPlusOnly(java.util.regex.Matcher);
     field public static final java.util.regex.Pattern DOMAIN_NAME;
     field public static final java.util.regex.Pattern EMAIL_ADDRESS;
-    field public static final java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
+    field public static final deprecated java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
     field public static final java.util.regex.Pattern IP_ADDRESS;
     field public static final java.util.regex.Pattern PHONE;
     field public static final deprecated java.util.regex.Pattern TOP_LEVEL_DOMAIN;
@@ -42634,6 +42838,27 @@
     method public void startTracking(android.view.KeyEvent, java.lang.Object);
   }
 
+  public final class KeyboardShortcutGroup implements android.os.Parcelable {
+    ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>);
+    ctor public KeyboardShortcutGroup(java.lang.CharSequence);
+    method public void addItem(android.view.KeyboardShortcutInfo);
+    method public int describeContents();
+    method public java.util.List<android.view.KeyboardShortcutInfo> getItems();
+    method public java.lang.CharSequence getLabel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR;
+  }
+
+  public final class KeyboardShortcutInfo implements android.os.Parcelable {
+    ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
+    method public int describeContents();
+    method public char getBaseCharacter();
+    method public java.lang.CharSequence getLabel();
+    method public int getModifiers();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR;
+  }
+
   public abstract class LayoutInflater {
     ctor protected LayoutInflater(android.content.Context);
     ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
@@ -42909,7 +43134,6 @@
     field public static final int AXIS_RX = 12; // 0xc
     field public static final int AXIS_RY = 13; // 0xd
     field public static final int AXIS_RZ = 14; // 0xe
-    field public static final int AXIS_SCROLL = 26; // 0x1a
     field public static final int AXIS_SIZE = 3; // 0x3
     field public static final int AXIS_THROTTLE = 19; // 0x13
     field public static final int AXIS_TILT = 25; // 0x19
@@ -44637,6 +44861,7 @@
     method public abstract boolean onMenuOpened(int, android.view.Menu);
     method public abstract void onPanelClosed(int, android.view.Menu);
     method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public abstract boolean onSearchRequested();
     method public abstract boolean onSearchRequested(android.view.SearchEvent);
     method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -45839,6 +46064,7 @@
   }
 
   public final class InputMethodManager {
+    method public void dispatchKeyEventFromInputMethod(android.view.View, android.view.KeyEvent);
     method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
     method public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
     method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
@@ -46467,7 +46693,7 @@
     method public abstract deprecated void setEnableSmoothTransition(boolean);
     method public abstract void setFantasyFontFamily(java.lang.String);
     method public abstract void setFixedFontFamily(java.lang.String);
-    method public abstract void setGeolocationDatabasePath(java.lang.String);
+    method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
     method public abstract void setGeolocationEnabled(boolean);
     method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
     method public abstract void setJavaScriptEnabled(boolean);
@@ -47154,12 +47380,18 @@
     method public int getThumbOffset();
     method public android.content.res.ColorStateList getThumbTintList();
     method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTickMark();
+    method public android.content.res.ColorStateList getTickMarkTintList();
+    method public android.graphics.PorterDuff.Mode getTickMarkTintMode();
     method public void setKeyProgressIncrement(int);
     method public void setSplitTrack(boolean);
     method public void setThumb(android.graphics.drawable.Drawable);
     method public void setThumbOffset(int);
     method public void setThumbTintList(android.content.res.ColorStateList);
     method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTickMark(android.graphics.drawable.Drawable);
+    method public void setTickMarkTintList(android.content.res.ColorStateList);
+    method public void setTickMarkTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public abstract class AbsSpinner extends android.widget.AdapterView {
@@ -52107,6 +52339,8 @@
   public class InternalError extends java.lang.VirtualMachineError {
     ctor public InternalError();
     ctor public InternalError(java.lang.String);
+    ctor public InternalError(java.lang.String, java.lang.Throwable);
+    ctor public InternalError(java.lang.Throwable);
   }
 
   public class InterruptedException extends java.lang.Exception {
@@ -52879,6 +53113,8 @@
   public abstract class VirtualMachineError extends java.lang.Error {
     ctor public VirtualMachineError();
     ctor public VirtualMachineError(java.lang.String);
+    ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+    ctor public VirtualMachineError(java.lang.Throwable);
   }
 
   public final class Void {
@@ -56621,6 +56857,7 @@
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
   }
 
   public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -56638,6 +56875,13 @@
   public abstract class CertPathBuilderSpi {
     ctor public CertPathBuilderSpi();
     method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public abstract interface CertPathChecker {
+    method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public abstract boolean isForwardCheckingSupported();
   }
 
   public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -56652,6 +56896,7 @@
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
     method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -56688,6 +56933,7 @@
 
   public abstract class CertPathValidatorSpi {
     ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
     method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -56846,9 +57092,10 @@
     method public java.security.cert.CertPath getCertPath();
   }
 
-  public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
     ctor protected PKIXCertPathChecker();
     method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
     method public java.lang.Object clone();
     method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
     method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -56908,6 +57155,30 @@
     enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
   }
 
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+    method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+    method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
   public abstract interface PolicyNode {
     method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
     method public abstract int getDepth();
@@ -57067,6 +57338,7 @@
     method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
     method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
     method public abstract int getVersion();
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract interface X509Extension {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 642d2a8..90a5dc7 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -190,6 +190,15 @@
 
 }
 
+package android.test.mock {
+
+  public class MockPackageManager extends android.content.pm.PackageManager {
+    method public deprecated java.lang.String getDefaultBrowserPackageName(int);
+    method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
+  }
+
+}
+
 package android.text.format {
 
   public class DateFormat {
diff --git a/api/test-current.txt b/api/test-current.txt
index 4546c5b..5e07343 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -67,7 +67,6 @@
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
-    field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT";
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
@@ -335,6 +334,7 @@
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844040; // 0x1010508
+    field public static final int canPerformGestures = 16844046; // 0x101050e
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
     field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
     field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -524,6 +524,7 @@
     field public static final int expandableListViewStyle = 16842863; // 0x101006f
     field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
     field public static final int exported = 16842768; // 0x1010010
+    field public static final int externalService = 16844047; // 0x101050f
     field public static final int extraTension = 16843371; // 0x101026b
     field public static final int extractNativeLibs = 16844010; // 0x10104ea
     field public static final int factor = 16843219; // 0x10101d3
@@ -1289,6 +1290,9 @@
     field public static final int thumbTint = 16843889; // 0x1010471
     field public static final int thumbTintMode = 16843890; // 0x1010472
     field public static final int thumbnail = 16843429; // 0x10102a5
+    field public static final int tickMark = 16844043; // 0x101050b
+    field public static final int tickMarkTint = 16844044; // 0x101050c
+    field public static final int tickMarkTintMode = 16844045; // 0x101050d
     field public static final int tileMode = 16843265; // 0x1010201
     field public static final int tileModeX = 16843895; // 0x1010477
     field public static final int tileModeY = 16843896; // 0x1010478
@@ -2548,6 +2552,7 @@
     field public static final int Widget_Material_ScrollView = 16974462; // 0x103027e
     field public static final int Widget_Material_SearchView = 16974463; // 0x103027f
     field public static final int Widget_Material_SeekBar = 16974464; // 0x1030280
+    field public static final int Widget_Material_SeekBar_Discrete = 16974553; // 0x10302d9
     field public static final int Widget_Material_SegmentedButton = 16974465; // 0x1030281
     field public static final int Widget_Material_Spinner = 16974467; // 0x1030283
     field public static final int Widget_Material_Spinner_Underlined = 16974468; // 0x1030284
@@ -2607,6 +2612,7 @@
 
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
+    method public final boolean dispatchGesture(android.accessibilityservice.GestureDescription, android.accessibilityservice.AccessibilityService.GestureResultCallback, android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -2646,6 +2652,12 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
   }
 
+  public static abstract class AccessibilityService.GestureResultCallback {
+    ctor public AccessibilityService.GestureResultCallback();
+    method public void onCancelled(android.accessibilityservice.GestureDescription);
+    method public void onCompleted(android.accessibilityservice.GestureDescription);
+  }
+
   public static final class AccessibilityService.MagnificationController {
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener);
     method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler);
@@ -2678,6 +2690,7 @@
     method public java.lang.String loadDescription(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
+    field public static final int CAPABILITY_CAN_PERFORM_GESTURES = 32; // 0x20
     field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
     field public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 8; // 0x8
     field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
@@ -2704,6 +2717,30 @@
     field public java.lang.String[] packageNames;
   }
 
+  public final class GestureDescription {
+    method public static android.accessibilityservice.GestureDescription createClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createLongClick(int, int);
+    method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long);
+    method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long);
+    method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int);
+    method public int getStrokeCount();
+    field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L
+    field public static final int MAX_STROKE_COUNT = 10; // 0xa
+  }
+
+  public static class GestureDescription.Builder {
+    ctor public GestureDescription.Builder();
+    method public android.accessibilityservice.GestureDescription.Builder addStroke(android.accessibilityservice.GestureDescription.StrokeDescription);
+    method public android.accessibilityservice.GestureDescription build();
+  }
+
+  public static class GestureDescription.StrokeDescription {
+    ctor public GestureDescription.StrokeDescription(android.graphics.Path, long, long);
+    method public long getDuration();
+    method public android.graphics.Path getPath();
+    method public long getStartTime();
+  }
+
 }
 
 package android.accounts {
@@ -2714,15 +2751,12 @@
     method public android.os.Bundle addAccountFromCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
-    method public android.os.Bundle finishSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public android.os.Bundle getAccountCredentialsForCloning(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
     method public android.os.Bundle getAccountRemovalAllowed(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
     method public final android.os.IBinder getIBinder();
     method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
-    method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
     field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
   }
@@ -2760,7 +2794,6 @@
     method public void clearPassword(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(java.lang.String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
-    method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public static android.accounts.AccountManager get(android.content.Context);
     method public android.accounts.Account[] getAccounts();
     method public android.accounts.Account[] getAccountsByType(java.lang.String);
@@ -2788,8 +2821,6 @@
     method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
     method public void setPassword(android.accounts.Account, java.lang.String);
     method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
-    method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
-    method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
     field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2806,8 +2837,6 @@
     field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
     field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
     field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
-    field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
-    field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
     field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
     field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
     field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3469,6 +3498,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.assist.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public android.net.Uri onProvideReferrer();
     method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
@@ -3639,6 +3669,9 @@
     method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle);
   }
 
+  public static abstract class ActivityManager.BugreportMode implements java.lang.annotation.Annotation {
+  }
+
   public static class ActivityManager.MemoryInfo implements android.os.Parcelable {
     ctor public ActivityManager.MemoryInfo();
     method public int describeContents();
@@ -4150,6 +4183,7 @@
     method public void onPanelClosed(int, android.view.Menu);
     method public boolean onPrepareOptionsMenu(android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public void onRestoreInstanceState(android.os.Bundle);
     method public android.os.Bundle onSaveInstanceState();
     method public boolean onSearchRequested(android.view.SearchEvent);
@@ -4229,7 +4263,7 @@
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_ID = "_id";
     field public static final java.lang.String COLUMN_LAST_MODIFIED_TIMESTAMP = "last_modified_timestamp";
-    field public static final java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
+    field public static final deprecated java.lang.String COLUMN_LOCAL_FILENAME = "local_filename";
     field public static final java.lang.String COLUMN_LOCAL_URI = "local_uri";
     field public static final java.lang.String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
     field public static final java.lang.String COLUMN_MEDIA_TYPE = "media_type";
@@ -5198,6 +5232,7 @@
     field public static final int SUPPRESSED_EFFECTS_UNSET = -1; // 0xffffffff
     field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
+    field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4
     field public final int priorityCallSenders;
     field public final int priorityCategories;
     field public final int priorityMessageSenders;
@@ -5501,8 +5536,10 @@
     method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
     method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+    method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean injectInputEvent(android.view.InputEvent, boolean);
     method public final boolean performGlobalAction(int);
+    method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
     method public boolean setRotation(int);
     method public void setRunAsMonkey(boolean);
@@ -5746,6 +5783,7 @@
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
+    method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
     method public boolean getAutoTimeRequired();
@@ -5753,14 +5791,17 @@
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public java.lang.String getDeviceOwnerLockScreenInfo();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
+    method public java.lang.String getLongSupportMessage(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
+    method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
     method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -5778,6 +5819,7 @@
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
+    method public java.lang.String getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
@@ -5806,6 +5848,7 @@
     method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean resetPassword(java.lang.String, int);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
+    method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
@@ -5814,11 +5857,13 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
     method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+    method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
     method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
     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);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
+    method public void setLongSupportMessage(android.content.ComponentName, java.lang.String);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -5843,6 +5888,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setShortSupportMessage(android.content.ComponentName, java.lang.String);
     method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
@@ -5858,6 +5904,7 @@
     field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
     field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
+    field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
     field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
     field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
     field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
@@ -6121,6 +6168,7 @@
     method public int describeContents();
     method public int getBackoffPolicy();
     method public android.os.PersistableBundle getExtras();
+    method public long getFlexMillis();
     method public int getId();
     method public long getInitialBackoffMillis();
     method public long getIntervalMillis();
@@ -6138,6 +6186,8 @@
     field public static final android.os.Parcelable.Creator<android.app.job.JobInfo> CREATOR;
     field public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 30000L; // 0x7530L
     field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIOD_MILLIS = 3600000L; // 0x36ee80L
     field public static final int NETWORK_TYPE_ANY = 1; // 0x1
     field public static final int NETWORK_TYPE_NONE = 0; // 0x0
     field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6151,6 +6201,7 @@
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
+    method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -6211,6 +6262,8 @@
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
     method public long getEndTimeStamp();
+    method public int getMetering();
+    method public int getRoaming();
     method public long getRxBytes();
     method public long getRxPackets();
     method public long getStartTimeStamp();
@@ -6218,6 +6271,12 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int METERING_ALL = -1; // 0xffffffff
+    field public static final int METERING_DEFAULT = 1; // 0x1
+    field public static final int METERING_METERED = 2; // 0x2
+    field public static final int ROAMING_ALL = -1; // 0xffffffff
+    field public static final int ROAMING_DEFAULT = 1; // 0x1
+    field public static final int ROAMING_ROAMING = 2; // 0x2
     field public static final int STATE_ALL = -1; // 0xffffffff
     field public static final int STATE_DEFAULT = 1; // 0x1
     field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -7144,6 +7203,15 @@
     field public static final int TYPE_SCO = 2; // 0x2
   }
 
+  public class OobData implements android.os.Parcelable {
+    ctor public OobData();
+    method public int describeContents();
+    method public byte[] getSecurityManagerTk();
+    method public void setSecurityManagerTk(byte[]);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+  }
+
 }
 
 package android.bluetooth.le {
@@ -7568,11 +7636,12 @@
     method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
   }
 
-  public class ContentProviderClient {
+  public class ContentProviderClient implements java.lang.AutoCloseable {
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException, android.os.RemoteException;
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]) throws android.os.RemoteException;
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
     method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
+    method public void close();
     method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
     method public android.content.ContentProvider getLocalContentProvider();
     method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -7586,7 +7655,7 @@
     method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
-    method public boolean release();
+    method public deprecated boolean release();
     method public final android.net.Uri uncanonicalize(android.net.Uri) throws android.os.RemoteException;
     method public int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
   }
@@ -7668,6 +7737,7 @@
     method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account, java.lang.String);
     method public java.util.List<android.content.UriPermission> getPersistedUriPermissions();
     method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String);
+    method public static java.lang.String[] getSyncAdapterPackagesForAuthorityAsUser(java.lang.String, int);
     method public static android.content.SyncAdapterType[] getSyncAdapterTypes();
     method public static boolean getSyncAutomatically(android.accounts.Account, java.lang.String);
     method public final java.lang.String getType(android.net.Uri);
@@ -7834,6 +7904,7 @@
     method public abstract java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public final java.lang.CharSequence getText(int);
     method public abstract android.content.res.Resources.Theme getTheme();
+    method public abstract int getUserId();
     method public abstract deprecated android.graphics.drawable.Drawable getWallpaper();
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
@@ -8016,6 +8087,7 @@
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
+    method public int getUserId();
     method public deprecated android.graphics.drawable.Drawable getWallpaper();
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
@@ -9068,6 +9140,8 @@
     ctor public ApplicationInfo(android.content.pm.ApplicationInfo);
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
+    method public boolean isPrivilegedApp();
+    method public boolean isSystemApp();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final android.os.Parcelable.Creator<android.content.pm.ApplicationInfo> CREATOR;
     field public static final int FLAG_ALLOW_BACKUP = 32768; // 0x8000
@@ -9429,9 +9503,8 @@
     method public abstract android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(android.content.ComponentName);
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
+    method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract byte[] getEphemeralCookie();
-    method public abstract int getEphemeralCookieMaxSizeBytes();
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9441,8 +9514,10 @@
     method public abstract java.lang.String getNameForUid(int);
     method public android.content.pm.PackageInfo getPackageArchiveInfo(java.lang.String, int);
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
+    method public abstract int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] getPackagesForUid(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -9463,7 +9538,6 @@
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
-    method public abstract boolean isEphemeralApplication();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9481,7 +9555,6 @@
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public abstract boolean setEphemeralCookie(byte[]);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -9513,6 +9586,7 @@
     field public static final java.lang.String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
     field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
+    field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
@@ -9571,8 +9645,8 @@
     field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
     field public static final int GET_ACTIVITIES = 1; // 0x1
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
-    field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
-    field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+    field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
+    field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
     field public static final int GET_GIDS = 256; // 0x100
     field public static final int GET_INSTRUMENTATION = 16; // 0x10
     field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -9584,13 +9658,17 @@
     field public static final int GET_SERVICES = 4; // 0x4
     field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
     field public static final int GET_SIGNATURES = 64; // 0x40
-    field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
+    field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+    field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
+    field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+    field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000
     field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000
-    field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000
-    field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000
+    field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000
+    field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
+    field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -10840,7 +10918,7 @@
     field public final int statusCode;
   }
 
-  public class DrmManagerClient {
+  public class DrmManagerClient implements java.lang.AutoCloseable {
     ctor public DrmManagerClient(android.content.Context);
     method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
     method public int acquireRights(android.drm.DrmInfoRequest);
@@ -10850,6 +10928,7 @@
     method public int checkRightsStatus(android.net.Uri);
     method public int checkRightsStatus(java.lang.String, int);
     method public int checkRightsStatus(android.net.Uri, int);
+    method public void close();
     method public android.drm.DrmConvertedStatus closeConvertSession(int);
     method public android.drm.DrmConvertedStatus convertData(int, byte[]);
     method public java.lang.String[] getAvailableDrmEngines();
@@ -10863,7 +10942,7 @@
     method public java.lang.String getOriginalMimeType(android.net.Uri);
     method public int openConvertSession(java.lang.String);
     method public int processDrmInfo(android.drm.DrmInfo);
-    method public void release();
+    method public deprecated void release();
     method public int removeAllRights();
     method public int removeRights(java.lang.String);
     method public int removeRights(android.net.Uri);
@@ -11267,14 +11346,14 @@
 
   public static class BitmapFactory.Options {
     ctor public BitmapFactory.Options();
-    method public void requestCancelDecode();
+    method public deprecated void requestCancelDecode();
     field public android.graphics.Bitmap inBitmap;
     field public int inDensity;
-    field public boolean inDither;
+    field public deprecated boolean inDither;
     field public deprecated boolean inInputShareable;
     field public boolean inJustDecodeBounds;
     field public boolean inMutable;
-    field public boolean inPreferQualityOverSpeed;
+    field public deprecated boolean inPreferQualityOverSpeed;
     field public android.graphics.Bitmap.Config inPreferredConfig;
     field public boolean inPremultiplied;
     field public deprecated boolean inPurgeable;
@@ -11283,7 +11362,7 @@
     field public int inScreenDensity;
     field public int inTargetDensity;
     field public byte[] inTempStorage;
-    field public boolean mCancel;
+    field public deprecated boolean mCancel;
     field public int outHeight;
     field public java.lang.String outMimeType;
     field public int outWidth;
@@ -12722,6 +12801,7 @@
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
+    field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
   }
 
   public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -18243,7 +18323,6 @@
     method public static java.lang.String getCanonicalID(java.lang.String, boolean[]);
     method public int getDSTSavings();
     method public static android.icu.util.TimeZone getDefault();
-    method public static int getDefaultTimeZoneType();
     method public final java.lang.String getDisplayName();
     method public final java.lang.String getDisplayName(java.util.Locale);
     method public final java.lang.String getDisplayName(android.icu.util.ULocale);
@@ -18267,8 +18346,6 @@
     method public abstract boolean inDaylightTime(java.util.Date);
     method public boolean isFrozen();
     method public boolean observesDaylightTime();
-    method public static synchronized void setDefault(android.icu.util.TimeZone);
-    method public static synchronized void setDefaultTimeZoneType(int);
     method public void setID(java.lang.String);
     method public abstract void setRawOffset(int);
     method public abstract boolean useDaylightTime();
@@ -18281,8 +18358,6 @@
     field public static final int SHORT_COMMONLY_USED = 6; // 0x6
     field public static final int SHORT_GENERIC = 2; // 0x2
     field public static final int SHORT_GMT = 4; // 0x4
-    field public static final int TIMEZONE_ICU = 0; // 0x0
-    field public static final int TIMEZONE_JDK = 1; // 0x1
     field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
     field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
   }
@@ -18378,8 +18453,6 @@
     method public static java.lang.String getVariant(java.lang.String);
     method public boolean isRightToLeft();
     method public static android.icu.util.ULocale minimizeSubtags(android.icu.util.ULocale);
-    method public static synchronized void setDefault(android.icu.util.ULocale);
-    method public static synchronized void setDefault(android.icu.util.ULocale.Category, android.icu.util.ULocale);
     method public android.icu.util.ULocale setKeywordValue(java.lang.String, java.lang.String);
     method public static java.lang.String setKeywordValue(java.lang.String, java.lang.String, java.lang.String);
     method public java.lang.String toLanguageTag();
@@ -19338,9 +19411,10 @@
     method public abstract void onAudioFocusChange(int);
   }
 
-  public class AudioRecord {
+  public class AudioRecord implements android.media.AudioRouting {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
-    method public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+    method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
+    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int getAudioFormat();
     method public int getAudioSessionId();
     method public int getAudioSource();
@@ -19364,7 +19438,8 @@
     method public int read(java.nio.ByteBuffer, int);
     method public int read(java.nio.ByteBuffer, int, int);
     method public void release();
-    method public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -19402,17 +19477,29 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
+  public abstract interface AudioRouting {
+    method public abstract void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
+    method public abstract android.media.AudioDeviceInfo getPreferredDevice();
+    method public abstract void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
+    method public abstract boolean setPreferredDevice(android.media.AudioDeviceInfo);
+  }
+
+  public static abstract interface AudioRouting.OnRoutingChangedListener {
+    method public abstract void onRoutingChanged(android.media.AudioRouting);
+  }
+
   public final class AudioTimestamp {
     ctor public AudioTimestamp();
     field public long framePosition;
     field public long nanoTime;
   }
 
-  public class AudioTrack {
+  public class AudioTrack implements android.media.AudioRouting {
     ctor public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException;
     ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException;
-    method public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+    method public deprecated void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler);
+    method public void addOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method public int attachAuxEffect(int);
     method public void flush();
     method public int getAudioFormat();
@@ -19442,7 +19529,8 @@
     method public void play() throws java.lang.IllegalStateException;
     method public void release();
     method public int reloadStaticData();
-    method public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public void removeOnRoutingListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public int setAuxEffectSendLevel(float);
     method public int setLoopPoints(int, int, int);
     method public int setNotificationMarkerPosition(int);
@@ -19495,8 +19583,8 @@
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
   }
 
-  public static abstract interface AudioTrack.OnRoutingChangedListener {
-    method public abstract void onRoutingChanged(android.media.AudioTrack);
+  public static abstract deprecated interface AudioTrack.OnRoutingChangedListener {
+    method public abstract deprecated void onRoutingChanged(android.media.AudioTrack);
   }
 
   public class CamcorderProfile {
@@ -20314,8 +20402,6 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22377,6 +22463,24 @@
     ctor public MtpConstants();
     method public static boolean isAbstractObject(int);
     field public static final int ASSOCIATION_TYPE_GENERIC_FOLDER = 1; // 0x1
+    field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
+    field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
+    field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
+    field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
+    field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
+    field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
+    field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
+    field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
+    field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
+    field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
+    field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
+    field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
+    field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
+    field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
+    field public static final int EVENT_STORE_FULL = 16394; // 0x400a
+    field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
+    field public static final int EVENT_UNDEFINED = 16384; // 0x4000
+    field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
     field public static final int FORMAT_3GP_CONTAINER = 47492; // 0xb984
     field public static final int FORMAT_AAC = 47363; // 0xb903
     field public static final int FORMAT_ABSTRACT_AUDIO_ALBUM = 47619; // 0xba03
@@ -22433,6 +22537,41 @@
     field public static final int FORMAT_WMV = 47489; // 0xb981
     field public static final int FORMAT_WPL_PLAYLIST = 47632; // 0xba10
     field public static final int FORMAT_XML_DOCUMENT = 47746; // 0xba82
+    field public static final int OPERATION_CLOSE_SESSION = 4099; // 0x1003
+    field public static final int OPERATION_COPY_OBJECT = 4122; // 0x101a
+    field public static final int OPERATION_DELETE_OBJECT = 4107; // 0x100b
+    field public static final int OPERATION_FORMAT_STORE = 4111; // 0x100f
+    field public static final int OPERATION_GET_DEVICE_INFO = 4097; // 0x1001
+    field public static final int OPERATION_GET_DEVICE_PROP_DESC = 4116; // 0x1014
+    field public static final int OPERATION_GET_DEVICE_PROP_VALUE = 4117; // 0x1015
+    field public static final int OPERATION_GET_NUM_OBJECTS = 4102; // 0x1006
+    field public static final int OPERATION_GET_OBJECT = 4105; // 0x1009
+    field public static final int OPERATION_GET_OBJECT_HANDLES = 4103; // 0x1007
+    field public static final int OPERATION_GET_OBJECT_INFO = 4104; // 0x1008
+    field public static final int OPERATION_GET_OBJECT_PROPS_SUPPORTED = 38913; // 0x9801
+    field public static final int OPERATION_GET_OBJECT_PROP_DESC = 38914; // 0x9802
+    field public static final int OPERATION_GET_OBJECT_PROP_VALUE = 38915; // 0x9803
+    field public static final int OPERATION_GET_OBJECT_REFERENCES = 38928; // 0x9810
+    field public static final int OPERATION_GET_PARTIAL_OBJECT = 4123; // 0x101b
+    field public static final int OPERATION_GET_STORAGE_INFO = 4101; // 0x1005
+    field public static final int OPERATION_GET_STORAGE_I_DS = 4100; // 0x1004
+    field public static final int OPERATION_GET_THUMB = 4106; // 0x100a
+    field public static final int OPERATION_INITIATE_CAPTURE = 4110; // 0x100e
+    field public static final int OPERATION_INITIATE_OPEN_CAPTURE = 4124; // 0x101c
+    field public static final int OPERATION_MOVE_OBJECT = 4121; // 0x1019
+    field public static final int OPERATION_OPEN_SESSION = 4098; // 0x1002
+    field public static final int OPERATION_POWER_DOWN = 4115; // 0x1013
+    field public static final int OPERATION_RESET_DEVICE = 4112; // 0x1010
+    field public static final int OPERATION_RESET_DEVICE_PROP_VALUE = 4119; // 0x1017
+    field public static final int OPERATION_SELF_TEST = 4113; // 0x1011
+    field public static final int OPERATION_SEND_OBJECT = 4109; // 0x100d
+    field public static final int OPERATION_SEND_OBJECT_INFO = 4108; // 0x100c
+    field public static final int OPERATION_SET_DEVICE_PROP_VALUE = 4118; // 0x1016
+    field public static final int OPERATION_SET_OBJECT_PROP_VALUE = 38916; // 0x9804
+    field public static final int OPERATION_SET_OBJECT_PROTECTION = 4114; // 0x1012
+    field public static final int OPERATION_SET_OBJECT_REFERENCES = 38929; // 0x9811
+    field public static final int OPERATION_SKIP = 38944; // 0x9820
+    field public static final int OPERATION_TERMINATE_OPEN_CAPTURE = 4120; // 0x1018
     field public static final int PROTECTION_STATUS_NONE = 0; // 0x0
     field public static final int PROTECTION_STATUS_NON_TRANSFERABLE_DATA = 32771; // 0x8003
     field public static final int PROTECTION_STATUS_READ_ONLY = 32769; // 0x8001
@@ -22450,6 +22589,7 @@
     method public int[] getObjectHandles(int, int, int);
     method public android.mtp.MtpObjectInfo getObjectInfo(int);
     method public long getParent(int);
+    method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
     method public long getStorageId(int);
     method public int[] getStorageIds();
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -22465,31 +22605,23 @@
   public class MtpDeviceInfo {
     method public final java.lang.String getManufacturer();
     method public final java.lang.String getModel();
+    method public final int[] getOperationsSupported();
     method public final java.lang.String getSerialNumber();
     method public final java.lang.String getVersion();
   }
 
   public class MtpEvent {
     ctor public MtpEvent();
+    method public int getDevicePropCode();
     method public int getEventCode();
-    field public static final int EVENT_CANCEL_TRANSACTION = 16385; // 0x4001
-    field public static final int EVENT_CAPTURE_COMPLETE = 16397; // 0x400d
-    field public static final int EVENT_DEVICE_INFO_CHANGED = 16392; // 0x4008
-    field public static final int EVENT_DEVICE_PROP_CHANGED = 16390; // 0x4006
-    field public static final int EVENT_DEVICE_RESET = 16395; // 0x400b
-    field public static final int EVENT_OBJECT_ADDED = 16386; // 0x4002
-    field public static final int EVENT_OBJECT_INFO_CHANGED = 16391; // 0x4007
-    field public static final int EVENT_OBJECT_PROP_CHANGED = 51201; // 0xc801
-    field public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 51202; // 0xc802
-    field public static final int EVENT_OBJECT_REFERENCES_CHANGED = 51203; // 0xc803
-    field public static final int EVENT_OBJECT_REMOVED = 16387; // 0x4003
-    field public static final int EVENT_REQUEST_OBJECT_TRANSFER = 16393; // 0x4009
-    field public static final int EVENT_STORAGE_INFO_CHANGED = 16396; // 0x400c
-    field public static final int EVENT_STORE_ADDED = 16388; // 0x4004
-    field public static final int EVENT_STORE_FULL = 16394; // 0x400a
-    field public static final int EVENT_STORE_REMOVED = 16389; // 0x4005
-    field public static final int EVENT_UNDEFINED = 16384; // 0x4000
-    field public static final int EVENT_UNREPORTED_STATUS = 16398; // 0x400e
+    method public int getObjectFormatCode();
+    method public int getObjectHandle();
+    method public int getObjectPropCode();
+    method public int getParameter1();
+    method public int getParameter2();
+    method public int getParameter3();
+    method public int getStorageId();
+    method public int getTransactionId();
   }
 
   public final class MtpObjectInfo {
@@ -27140,7 +27272,7 @@
     ctor public GLException(int, java.lang.String);
   }
 
-  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback {
+  public class GLSurfaceView extends android.view.SurfaceView implements android.view.SurfaceHolder.Callback2 {
     ctor public GLSurfaceView(android.content.Context);
     ctor public GLSurfaceView(android.content.Context, android.util.AttributeSet);
     method public int getDebugFlags();
@@ -27164,6 +27296,7 @@
     method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
     method public void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
     field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
     field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
     field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -28364,6 +28497,7 @@
   public final class UserHandle implements android.os.Parcelable {
     ctor public UserHandle(android.os.Parcel);
     method public int describeContents();
+    method public static int getAppId(int);
     method public static android.os.UserHandle readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     method public static void writeToParcel(android.os.UserHandle, android.os.Parcel);
@@ -28371,6 +28505,7 @@
   }
 
   public class UserManager {
+    method public static android.content.Intent createUserCreationIntent(java.lang.String, java.lang.String, java.lang.String, android.os.PersistableBundle);
     method public android.os.Bundle getApplicationRestrictions(java.lang.String);
     method public long getSerialNumberForUser(android.os.UserHandle);
     method public int getUserCount();
@@ -28409,6 +28544,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
     field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
     field public static final java.lang.String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
+    field public static final java.lang.String DISALLOW_DATA_ROAMING = "no_data_roaming";
     field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
     field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_FUN = "no_fun";
@@ -29282,6 +29418,22 @@
     field public static final java.lang.String _ID = "_id";
   }
 
+  public class BlockedNumberContract {
+    method public static boolean isBlocked(android.content.Context, java.lang.String);
+    field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
+    field public static final android.net.Uri AUTHORITY_URI;
+  }
+
+  public static class BlockedNumberContract.BlockedNumbers {
+    field public static final java.lang.String COLUMN_E164_NUMBER = "e164_number";
+    field public static final java.lang.String COLUMN_ID = "_id";
+    field public static final java.lang.String COLUMN_ORIGINAL_NUMBER = "original_number";
+    field public static final java.lang.String COLUMN_STRIPPED_NUMBER = "stripped_number";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
   public class Browser {
     ctor public Browser();
     method public static final void sendString(android.content.Context, java.lang.String);
@@ -30779,7 +30931,7 @@
     field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
     field public static final java.lang.String COLUMN_SIZE = "_size";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
-    field public static final int FLAG_ARCHIVE = 2048; // 0x800
+    field public static final int FLAG_ARCHIVE = 1024; // 0x400
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -30788,9 +30940,8 @@
     field public static final int FLAG_SUPPORTS_MOVE = 256; // 0x100
     field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
     field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
-    field public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 512; // 0x200
     field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
-    field public static final int FLAG_VIRTUAL_DOCUMENT = 1024; // 0x400
+    field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
     field public static final java.lang.String MIME_TYPE_DIR = "vnd.android.document/directory";
   }
 
@@ -31234,6 +31385,7 @@
     field public static final java.lang.String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS";
     field public static final java.lang.String ACTION_SETTINGS = "android.settings.SETTINGS";
+    field public static final java.lang.String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
     field public static final java.lang.String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
     field public static final java.lang.String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS";
     field public static final java.lang.String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS";
@@ -31280,6 +31432,7 @@
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
     field public static final java.lang.String DEBUG_APP = "debug_app";
@@ -31388,6 +31541,7 @@
     field public static final deprecated java.lang.String TTS_USE_DEFAULTS = "tts_use_defaults";
     field public static final deprecated java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
     field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
+    field public static final java.lang.String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
     field public static final deprecated java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
     field public static final deprecated java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
     field public static final deprecated java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
@@ -31771,6 +31925,7 @@
     field public static final int RESULT_SMS_OUT_OF_MEMORY = 3; // 0x3
     field public static final int RESULT_SMS_UNSUPPORTED = 4; // 0x4
     field public static final java.lang.String SIM_FULL_ACTION = "android.provider.Telephony.SIM_FULL";
+    field public static final java.lang.String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION";
     field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED";
     field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER";
     field public static final java.lang.String SMS_EMERGENCY_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
@@ -33107,6 +33262,7 @@
   public class NetworkSecurityPolicy {
     method public static android.security.NetworkSecurityPolicy getInstance();
     method public boolean isCleartextTrafficPermitted();
+    method public boolean isCleartextTrafficPermitted(java.lang.String);
   }
 
 }
@@ -33401,6 +33557,7 @@
     method public boolean onMenuOpened(int, android.view.Menu);
     method public void onPanelClosed(int, android.view.Menu);
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public boolean onSearchRequested(android.view.SearchEvent);
     method public boolean onSearchRequested();
     method public void onWakeUp();
@@ -33495,24 +33652,18 @@
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+    field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
     field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  public class NotificationAdjustment implements android.os.Parcelable {
-    ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
-  }
-
   public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
     ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
     method public final void clearAnnotation(java.lang.String);
     method public void onNotificationActionClick(java.lang.String, long, int);
     method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
     method public void onNotificationRemoved(java.lang.String, long, int);
     method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
     method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33528,10 +33679,15 @@
     field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
     field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
     field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_TOPIC_BANNED = 14; // 0xe
     field public static final int REASON_USER_STOPPED = 6; // 0x6
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
   }
 
+  public class NotificationAssistantService.Adjustment {
+    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+  }
+
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
@@ -33554,6 +33710,8 @@
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
+    method public static final void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
+    method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
     field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
@@ -33565,6 +33723,7 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
     field public static final int SUPPRESSED_EFFECT_LIGHTS = 1; // 0x1
     field public static final int SUPPRESSED_EFFECT_PEEK = 2; // 0x2
+    field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 4; // 0x4
   }
 
   public static class NotificationListenerService.Ranking {
@@ -33621,25 +33780,37 @@
     method public java.lang.CharSequence getContentDescription();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
+    method public int getState();
     method public void setContentDescription(java.lang.CharSequence);
     method public void setIcon(android.graphics.drawable.Icon);
     method public void setLabel(java.lang.CharSequence);
+    method public void setState(int);
     method public void updateTile();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+    field public static final int STATE_ACTIVE = 2; // 0x2
+    field public static final int STATE_INACTIVE = 1; // 0x1
+    field public static final int STATE_UNAVAILABLE = 0; // 0x0
   }
 
   public class TileService extends android.app.Service {
     ctor public TileService();
     method public final android.service.quicksettings.Tile getQsTile();
+    method public final boolean isLocked();
+    method public final boolean isSecure();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public void onTileAdded();
+    method public int onTileAdded();
     method public void onTileRemoved();
+    method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void showDialog(android.app.Dialog);
+    method public final void startActivityAndCollapse(android.content.Intent);
+    method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
+    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
+    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
   }
 
 }
@@ -34900,6 +35071,7 @@
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_WIFI = 8; // 0x8
+    field public static final int PROPERTY_WORK_CALL = 32; // 0x20
   }
 
   public final class CallAudioState implements android.os.Parcelable {
@@ -36044,6 +36216,7 @@
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
+    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method public boolean iccCloseLogicalChannel(int);
@@ -36056,6 +36229,7 @@
     method public boolean isSmsCapable();
     method public boolean isTtyModeSupported();
     method public boolean isVoiceCapable();
+    method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
@@ -36612,6 +36786,7 @@
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
+    method public int getUserId();
     method public android.graphics.drawable.Drawable getWallpaper();
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
@@ -36738,10 +36913,8 @@
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
-    method public java.lang.String getDefaultBrowserPackageName(int);
+    method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public byte[] getEphemeralCookie();
-    method public int getEphemeralCookieMaxSizeBytes();
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36750,8 +36923,10 @@
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInstaller getPackageInstaller();
+    method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] getPackagesForUid(int);
     method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -36772,7 +36947,6 @@
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
-    method public boolean isEphemeralApplication();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36790,8 +36964,6 @@
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
-    method public boolean setDefaultBrowserPackageName(java.lang.String, int);
-    method public boolean setEphemeralCookie(byte[]);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
@@ -37994,9 +38166,11 @@
 
   public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public LocaleSpan(java.util.Locale);
+    ctor public LocaleSpan(android.util.LocaleList);
     ctor public LocaleSpan(android.os.Parcel);
     method public int describeContents();
     method public java.util.Locale getLocale();
+    method public android.util.LocaleList getLocales();
     method public int getSpanTypeId();
     method public void updateDrawState(android.text.TextPaint);
     method public void updateMeasureState(android.text.TextPaint);
@@ -38102,7 +38276,8 @@
     ctor public SuggestionSpan(android.os.Parcel);
     method public int describeContents();
     method public int getFlags();
-    method public java.lang.String getLocale();
+    method public deprecated java.lang.String getLocale();
+    method public java.util.Locale getLocaleObject();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
     method public void setFlags(int);
@@ -39022,7 +39197,9 @@
     method public static android.util.LocaleList getEmptyLocaleList();
     method public java.util.Locale getFirstMatch(java.lang.String[]);
     method public java.util.Locale getPrimary();
+    method public int indexOf(java.util.Locale);
     method public boolean isEmpty();
+    method public static void setDefault(android.util.LocaleList);
     method public int size();
     method public java.lang.String toLanguageTags();
     method public void writeToParcel(android.os.Parcel, int);
@@ -39180,7 +39357,7 @@
     method public static final java.lang.String digitsAndPlusOnly(java.util.regex.Matcher);
     field public static final java.util.regex.Pattern DOMAIN_NAME;
     field public static final java.util.regex.Pattern EMAIL_ADDRESS;
-    field public static final java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
+    field public static final deprecated java.lang.String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
     field public static final java.util.regex.Pattern IP_ADDRESS;
     field public static final java.util.regex.Pattern PHONE;
     field public static final deprecated java.util.regex.Pattern TOP_LEVEL_DOMAIN;
@@ -40284,6 +40461,27 @@
     method public void startTracking(android.view.KeyEvent, java.lang.Object);
   }
 
+  public final class KeyboardShortcutGroup implements android.os.Parcelable {
+    ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>);
+    ctor public KeyboardShortcutGroup(java.lang.CharSequence);
+    method public void addItem(android.view.KeyboardShortcutInfo);
+    method public int describeContents();
+    method public java.util.List<android.view.KeyboardShortcutInfo> getItems();
+    method public java.lang.CharSequence getLabel();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR;
+  }
+
+  public final class KeyboardShortcutInfo implements android.os.Parcelable {
+    ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
+    method public int describeContents();
+    method public char getBaseCharacter();
+    method public java.lang.CharSequence getLabel();
+    method public int getModifiers();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR;
+  }
+
   public abstract class LayoutInflater {
     ctor protected LayoutInflater(android.content.Context);
     ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
@@ -40559,7 +40757,6 @@
     field public static final int AXIS_RX = 12; // 0xc
     field public static final int AXIS_RY = 13; // 0xd
     field public static final int AXIS_RZ = 14; // 0xe
-    field public static final int AXIS_SCROLL = 26; // 0x1a
     field public static final int AXIS_SIZE = 3; // 0x3
     field public static final int AXIS_THROTTLE = 19; // 0x13
     field public static final int AXIS_TILT = 25; // 0x19
@@ -42286,6 +42483,7 @@
     method public abstract boolean onMenuOpened(int, android.view.Menu);
     method public abstract void onPanelClosed(int, android.view.Menu);
     method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
+    method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
     method public abstract boolean onSearchRequested();
     method public abstract boolean onSearchRequested(android.view.SearchEvent);
     method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -43486,6 +43684,7 @@
   }
 
   public final class InputMethodManager {
+    method public void dispatchKeyEventFromInputMethod(android.view.View, android.view.KeyEvent);
     method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
     method public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
     method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
@@ -44046,7 +44245,7 @@
     method public abstract deprecated void setEnableSmoothTransition(boolean);
     method public abstract void setFantasyFontFamily(java.lang.String);
     method public abstract void setFixedFontFamily(java.lang.String);
-    method public abstract void setGeolocationDatabasePath(java.lang.String);
+    method public abstract deprecated void setGeolocationDatabasePath(java.lang.String);
     method public abstract void setGeolocationEnabled(boolean);
     method public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean);
     method public abstract void setJavaScriptEnabled(boolean);
@@ -44488,12 +44687,18 @@
     method public int getThumbOffset();
     method public android.content.res.ColorStateList getThumbTintList();
     method public android.graphics.PorterDuff.Mode getThumbTintMode();
+    method public android.graphics.drawable.Drawable getTickMark();
+    method public android.content.res.ColorStateList getTickMarkTintList();
+    method public android.graphics.PorterDuff.Mode getTickMarkTintMode();
     method public void setKeyProgressIncrement(int);
     method public void setSplitTrack(boolean);
     method public void setThumb(android.graphics.drawable.Drawable);
     method public void setThumbOffset(int);
     method public void setThumbTintList(android.content.res.ColorStateList);
     method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
+    method public void setTickMark(android.graphics.drawable.Drawable);
+    method public void setTickMarkTintList(android.content.res.ColorStateList);
+    method public void setTickMarkTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public abstract class AbsSpinner extends android.widget.AdapterView {
@@ -49441,6 +49646,8 @@
   public class InternalError extends java.lang.VirtualMachineError {
     ctor public InternalError();
     ctor public InternalError(java.lang.String);
+    ctor public InternalError(java.lang.String, java.lang.Throwable);
+    ctor public InternalError(java.lang.Throwable);
   }
 
   public class InterruptedException extends java.lang.Exception {
@@ -50213,6 +50420,8 @@
   public abstract class VirtualMachineError extends java.lang.Error {
     ctor public VirtualMachineError();
     ctor public VirtualMachineError(java.lang.String);
+    ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+    ctor public VirtualMachineError(java.lang.Throwable);
   }
 
   public final class Void {
@@ -53955,6 +54164,7 @@
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
   }
 
   public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -53972,6 +54182,13 @@
   public abstract class CertPathBuilderSpi {
     ctor public CertPathBuilderSpi();
     method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public abstract interface CertPathChecker {
+    method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public abstract boolean isForwardCheckingSupported();
   }
 
   public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -53986,6 +54203,7 @@
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
     method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
     method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
     method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54022,6 +54240,7 @@
 
   public abstract class CertPathValidatorSpi {
     ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
     method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
   }
 
@@ -54180,9 +54399,10 @@
     method public java.security.cert.CertPath getCertPath();
   }
 
-  public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
     ctor protected PKIXCertPathChecker();
     method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
     method public java.lang.Object clone();
     method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
     method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -54242,6 +54462,30 @@
     enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
   }
 
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+    method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+    method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
   public abstract interface PolicyNode {
     method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
     method public abstract int getDepth();
@@ -54401,6 +54645,7 @@
     method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
     method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
     method public abstract int getVersion();
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
   }
 
   public abstract interface X509Extension {
diff --git a/api/test-removed.txt b/api/test-removed.txt
index f12e61e..6b7961e 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -199,6 +199,15 @@
 
 }
 
+package android.test.mock {
+
+  public class MockPackageManager extends android.content.pm.PackageManager {
+    method public deprecated java.lang.String getDefaultBrowserPackageName(int);
+    method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
+  }
+
+}
+
 package android.text.format {
 
   public class DateFormat {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 4f7a109..72e8c3b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -159,6 +159,7 @@
                 "       am stack start <DISPLAY_ID> <INTENT>\n" +
                 "       am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
                 "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
+                "       am stack resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]\n" +
                 "       am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
                 "       am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "       am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
@@ -299,7 +300,11 @@
                 "am stack movetask: move <TASK_ID> from its current stack to the top (true) or" +
                 "   bottom (false) of <STACK_ID>.\n" +
                 "\n" +
-                "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>." +
+                "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.\n" +
+                "\n" +
+                "am stack resize-docked-stack: change docked stack to <LEFT,TOP,RIGHT,BOTTOM>\n" +
+                "   and supplying temporary different task bounds indicated by\n" +
+                "   <TASK_LEFT,TOP,RIGHT,BOTTOM>\n" +
                 "\n" +
                 "am stack size-docked-stack-test: test command for sizing docked stack by\n" +
                 "   <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" +
@@ -1075,16 +1080,16 @@
 
     private void runBugReport() throws Exception {
         String opt;
-        boolean progress = false;
+        int bugreportType = ActivityManager.BUGREPORT_OPTION_FULL;
         while ((opt=nextOption()) != null) {
             if (opt.equals("--progress")) {
-                progress = true;
+                bugreportType = ActivityManager.BUGREPORT_OPTION_INTERACTIVE;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
             }
         }
-        mAm.requestBugReport(progress);
+        mAm.requestBugReport(bugreportType);
         System.out.println("Your lovely bug report is being created; please be patient.");
     }
 
@@ -1683,6 +1688,9 @@
             case "resize":
                 runStackResize();
                 break;
+            case "resize-docked-stack":
+                runStackResizeDocked();
+                break;
             case "positiontask":
                 runStackPositionTask();
                 break;
@@ -1751,6 +1759,20 @@
         resizeStack(stackId, bounds, 0);
     }
 
+    private void runStackResizeDocked() throws Exception {
+        final Rect bounds = getBounds();
+        final Rect taskBounds = getBounds();
+        if (bounds == null || taskBounds == null) {
+            System.err.println("Error: invalid input bounds");
+            return;
+        }
+        try {
+            mAm.resizeDockedStack(bounds, taskBounds, null, null, null);
+        } catch (RemoteException e) {
+            showError("Error: resizing docked stack " + e);
+        }
+    }
+
     private void resizeStack(int stackId, Rect bounds, int delayMs) throws Exception {
         if (bounds == null) {
             showError("Error: invalid input bounds");
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 468c145..3293c26 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,11 +16,13 @@
 
 package android.accessibilityservice;
 
+import android.accessibilityservice.GestureDescription.MotionEventGenerator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ParceledListSlice;
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.IBinder;
@@ -29,8 +31,11 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityEvent;
@@ -41,10 +46,7 @@
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
 
 /**
  * An accessibility service runs in the background and receives callbacks by the system
@@ -376,6 +378,7 @@
         public boolean onKeyEvent(KeyEvent event);
         public void onMagnificationChanged(@NonNull Region region,
                 float scale, float centerX, float centerY);
+        public void onPerformGestureResult(int sequence, boolean completedSuccessfully);
     }
 
     private int mConnectionId;
@@ -388,6 +391,12 @@
 
     private MagnificationController mMagnificationController;
 
+    private int mGestureStatusCallbackSequence;
+
+    private SparseArray<GestureResultCallbackInfo> mGestureStatusCallbackInfos;
+
+    private final Object mLock = new Object();
+
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
      *
@@ -551,6 +560,88 @@
         return mMagnificationController;
     }
 
+    /**
+     * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
+     * the user, this service, or another service, will be cancelled.
+     * <p>
+     * <strong>Note:</strong> In order to dispatch gestures, your service
+     * must declare the capability by setting the
+     * {@link android.R.styleable#AccessibilityService_canPerformGestures}
+     * property in its meta-data. For more information, see
+     * {@link #SERVICE_META_DATA}.
+     *
+     * @param gesture The gesture to dispatch
+     * @param callback The object to call back when the status of the gesture is known. If
+     * {@code null}, no status is reported.
+     * @param handler The handler on which to call back the {@code callback} object. If
+     * {@code null}, the object is called back on the service's main thread.
+     *
+     * @return {@code true} if the gesture is dispatched, {@code false} if not.
+     */
+    public final boolean dispatchGesture(@NonNull GestureDescription gesture,
+            @Nullable GestureResultCallback callback,
+            @Nullable Handler handler) {
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance().getConnection(
+                        mConnectionId);
+        if (connection == null) {
+            return false;
+        }
+        List<MotionEvent> events = MotionEventGenerator.getMotionEventsFromGestureDescription(
+                gesture, 100);
+        try {
+            synchronized (mLock) {
+                connection.sendMotionEvents(++mGestureStatusCallbackSequence,
+                        new ParceledListSlice<>(events));
+                if (callback != null) {
+                    if (mGestureStatusCallbackInfos == null) {
+                        mGestureStatusCallbackInfos = new SparseArray<>();
+                    }
+                    GestureResultCallbackInfo callbackInfo = new GestureResultCallbackInfo(gesture,
+                            callback, handler);
+                    mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
+                }
+            }
+        } catch (RemoteException re) {
+            throw new RuntimeException(re);
+        }
+        return true;
+    }
+
+    void onPerformGestureResult(int sequence, final boolean completedSuccessfully) {
+        if (mGestureStatusCallbackInfos == null) {
+            return;
+        }
+        GestureResultCallbackInfo callbackInfo;
+        synchronized (mLock) {
+            callbackInfo = mGestureStatusCallbackInfos.get(sequence);
+        }
+        final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
+        if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
+                && (callbackInfo.callback != null)) {
+            if (callbackInfo.handler != null) {
+                callbackInfo.handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (completedSuccessfully) {
+                            finalCallbackInfo.callback
+                                    .onCompleted(finalCallbackInfo.gestureDescription);
+                        } else {
+                            finalCallbackInfo.callback
+                                    .onCancelled(finalCallbackInfo.gestureDescription);
+                        }
+                    }
+                });
+                return;
+            }
+            if (completedSuccessfully) {
+                callbackInfo.callback.onCompleted(callbackInfo.gestureDescription);
+            } else {
+                callbackInfo.callback.onCancelled(callbackInfo.gestureDescription);
+            }
+        }
+    }
+
     private void onMagnificationChanged(@NonNull Region region, float scale,
             float centerX, float centerY) {
         if (mMagnificationController != null) {
@@ -1082,6 +1173,11 @@
                     float scale, float centerX, float centerY) {
                 AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
             }
+
+            @Override
+            public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
+                AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
+            }
         });
     }
 
@@ -1100,6 +1196,7 @@
         private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
         private static final int DO_ON_KEY_EVENT = 6;
         private static final int DO_ON_MAGNIFICATION_CHANGED = 7;
+        private static final int DO_GESTURE_COMPLETE = 8;
 
         private final HandlerCaller mCaller;
 
@@ -1158,6 +1255,12 @@
             mCaller.sendMessage(message);
         }
 
+        public void onPerformGestureResult(int sequence, boolean successfully) {
+            Message message = mCaller.obtainMessageII(DO_GESTURE_COMPLETE, sequence,
+                    successfully ? 1 : 0);
+            mCaller.sendMessage(message);
+        }
+
         @Override
         public void executeMessage(Message message) {
             switch (message.what) {
@@ -1242,9 +1345,47 @@
                     mCallback.onMagnificationChanged(region, scale, centerX, centerY);
                 } return;
 
+                case DO_GESTURE_COMPLETE: {
+                    final boolean successfully = message.arg2 == 1;
+                    mCallback.onPerformGestureResult(message.arg1, successfully);
+                } return;
+
                 default :
                     Log.w(LOG_TAG, "Unknown message type " + message.what);
             }
         }
     }
+
+    /**
+     * Class used to report status of dispatched gestures
+     */
+    public static abstract class GestureResultCallback {
+        /** Called when the gesture has completed successfully
+         *
+         * @param gestureDescription The description of the gesture that completed.
+         */
+        public void onCompleted(GestureDescription gestureDescription) {
+        }
+
+        /** Called when the gesture was cancelled
+         *
+         * @param gestureDescription The description of the gesture that was cancelled.
+         */
+        public void onCancelled(GestureDescription gestureDescription) {
+        }
+    }
+
+    /* Object to keep track of gesture result callbacks */
+    private static class GestureResultCallbackInfo {
+        GestureDescription gestureDescription;
+        GestureResultCallback callback;
+        Handler handler;
+
+        GestureResultCallbackInfo(GestureDescription gestureDescription,
+                GestureResultCallback callback, Handler handler) {
+            this.gestureDescription = gestureDescription;
+            this.callback = callback;
+            this.handler = handler;
+        }
+    }
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 2c98fef..079bdfc 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -110,6 +110,12 @@
      */
     public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0x00000010;
 
+    /**
+     * Capability: This accessibility service can perform gestures.
+     * @see android.R.styleable#AccessibilityService_canPerformGestures
+     */
+    public static final int CAPABILITY_CAN_PERFORM_GESTURES = 0x00000020;
+
     private static final SparseArray<CapabilityInfo> sAvailableCapabilityInfos =
             new SparseArray<CapabilityInfo>();
     static {
@@ -133,6 +139,10 @@
                 new CapabilityInfo(CAPABILITY_CAN_CONTROL_MAGNIFICATION,
                         R.string.capability_title_canControlMagnification,
                         R.string.capability_desc_canControlMagnification));
+        sAvailableCapabilityInfos.put(CAPABILITY_CAN_PERFORM_GESTURES,
+                new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES,
+                        R.string.capability_title_canPerformGestures,
+                        R.string.capability_desc_canPerformGestures));
     }
 
     /**
@@ -276,12 +286,7 @@
     /**
      * This flag requests from the system to filter key events. If this flag
      * is set the accessibility service will receive the key events before
-     * applications allowing it implement global shortcuts. Setting this flag
-     * does not guarantee that this service will filter key events since only
-     * one service can do so at any given time. This avoids user confusion due
-     * to behavior change in case different key filtering services are enabled.
-     * If there is already another key filtering service enabled, this one will
-     * not receive key events.
+     * applications allowing it implement global shortcuts.
      * <p>
      * Services that want to set this flag have to declare this capability
      * in their meta-data by setting the attribute {@link android.R.attr
@@ -516,6 +521,10 @@
                     .AccessibilityService_canControlMagnification, false)) {
                 mCapabilities |= CAPABILITY_CAN_CONTROL_MAGNIFICATION;
             }
+            if (asAttributes.getBoolean(com.android.internal.R.styleable
+                    .AccessibilityService_canPerformGestures, false)) {
+                mCapabilities |= CAPABILITY_CAN_PERFORM_GESTURES;
+            }
             TypedValue peekedValue = asAttributes.peekValue(
                     com.android.internal.R.styleable.AccessibilityService_description);
             if (peekedValue != null) {
@@ -616,6 +625,8 @@
      * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
      * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
      * @see #CAPABILITY_FILTER_KEY_EVENTS
+     * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
+     * @see #CAPABILITY_CAN_PERFORM_GESTURES
      */
     public int getCapabilities() {
         return mCapabilities;
@@ -631,6 +642,8 @@
      * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
      * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
      * @see #CAPABILITY_FILTER_KEY_EVENTS
+     * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION
+     * @see #CAPABILITY_CAN_PERFORM_GESTURES
      *
      * @hide
      */
@@ -933,6 +946,8 @@
                 return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
             case CAPABILITY_CAN_CONTROL_MAGNIFICATION:
                 return "CAPABILITY_CAN_CONTROL_MAGNIFICATION";
+            case CAPABILITY_CAN_PERFORM_GESTURES:
+                return "CAPABILITY_CAN_PERFORM_GESTURES";
             default:
                 return "UNKNOWN";
         }
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
new file mode 100644
index 0000000..14aabcf
--- /dev/null
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.RectF;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Accessibility services with the
+ * {@link android.R.styleable#AccessibilityService_canPerformGestures} property can dispatch
+ * gestures. This class describes those gestures. Gestures are made up of one or more strokes.
+ * Gestures are immutable; use the {@code create} methods to get common gesture, or a
+ * {@code Builder} to create a new one.
+ * <p>
+ * Spatial dimensions throughout are in screen pixels. Time is measured in milliseconds.
+ */
+public final class GestureDescription {
+    /** Gestures may contain no more than this many strokes */
+    public static final int MAX_STROKE_COUNT = 10;
+
+    /**
+     * Upper bound on total gesture duration. Nearly all gestures will be much shorter.
+     */
+    public static final long MAX_GESTURE_DURATION_MS = 60 * 1000;
+
+    private final List<StrokeDescription> mStrokes = new ArrayList<>();
+    private final float[] mTempPos = new float[2];
+
+    /**
+     * Create a description of a click gesture
+     *
+     * @param x The x coordinate to click. Must not be negative.
+     * @param y The y coordinate to click. Must not be negative.
+     *
+     * @return A description of a click at (x, y)
+     */
+    public static GestureDescription createClick(@IntRange(from = 0) int x,
+            @IntRange(from = 0) int y) {
+        Path clickPath = new Path();
+        clickPath.moveTo(x, y);
+        clickPath.lineTo(x + 1, y);
+        return new GestureDescription(
+                new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout()));
+    }
+
+    /**
+     * Create a description of a long click gesture
+     *
+     * @param x The x coordinate to click. Must not be negative.
+     * @param y The y coordinate to click. Must not be negative.
+     *
+     * @return A description of a click at (x, y)
+     */
+    public static GestureDescription createLongClick(@IntRange(from = 0) int x,
+            @IntRange(from = 0) int y) {
+        Path clickPath = new Path();
+        clickPath.moveTo(x, y);
+        clickPath.lineTo(x + 1, y);
+        int longPressTime = ViewConfiguration.getLongPressTimeout();
+        return new GestureDescription(
+                new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2)));
+    }
+
+    /**
+     * Create a description of a swipe gesture
+     *
+     * @param startX The x coordinate of the starting point. Must not be negative.
+     * @param startY The y coordinate of the starting point. Must not be negative.
+     * @param endX The x coordinate of the ending point. Must not be negative.
+     * @param endY The y coordinate of the ending point. Must not be negative.
+     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
+     *
+     * @return A description of a swipe from ({@code startX}, {@code startY}) to
+     * ({@code endX}, {@code endY}) that takes {@code duration} milliseconds. Returns {@code null}
+     * if the path specified for the swipe is invalid.
+     */
+    public static GestureDescription createSwipe(@IntRange(from = 0) int startX,
+            @IntRange(from = 0) int startY,
+            @IntRange(from = 0) int endX,
+            @IntRange(from = 0) int endY,
+            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+        Path swipePath = new Path();
+        swipePath.moveTo(startX, startY);
+        swipePath.lineTo(endX, endY);
+        return new GestureDescription(new StrokeDescription(swipePath, 0, duration));
+    }
+
+    /**
+     * Create a description for a pinch (or zoom) gesture.
+     *
+     * @param centerX The x coordinate of the center of the pinch. Must not be negative.
+     * @param centerY The y coordinate of the center of the pinch. Must not be negative.
+     * @param startSpacing The spacing of the touch points at the beginning of the gesture. Must not
+     * be negative.
+     * @param endSpacing The spacing of the touch points at the end of the gesture. Must not be
+     * negative.
+     * @param orientation The angle, in degrees, of the gesture. 0 represents a horizontal pinch
+     * @param duration The time, in milliseconds, to complete the gesture. Must not be negative.
+     *
+     * @return A description of a pinch centered at ({code centerX}, {@code centerY}) that starts
+     * with the touch points spaced by {@code startSpacing} and ends with them spaced by
+     * {@code endSpacing} that lasts {@code duration} ms. Returns {@code null} if either path
+     * specified for the pinch is invalid.
+     */
+    public static GestureDescription createPinch(@IntRange(from = 0) int centerX,
+            @IntRange(from = 0) int centerY,
+            @IntRange(from = 0) int startSpacing,
+            @IntRange(from = 0) int endSpacing,
+            float orientation,
+            @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+        if ((startSpacing < 0) || (endSpacing < 0)) {
+            throw new IllegalArgumentException("Pinch spacing cannot be negative");
+        }
+        float[] startPoint1 = new float[2];
+        float[] endPoint1 = new float[2];
+        float[] startPoint2 = new float[2];
+        float[] endPoint2 = new float[2];
+
+        /* Build points for a horizontal gesture centered at the origin */
+        startPoint1[0] = startSpacing / 2;
+        startPoint1[1] = 0;
+        endPoint1[0] = endSpacing / 2;
+        endPoint1[1] = 0;
+        startPoint2[0] = -startSpacing / 2;
+        startPoint2[1] = 0;
+        endPoint2[0] = -endSpacing / 2;
+        endPoint2[1] = 0;
+
+        /* Rotate and translate the points */
+        Matrix matrix = new Matrix();
+        matrix.setRotate(orientation);
+        matrix.postTranslate(centerX, centerY);
+        matrix.mapPoints(startPoint1);
+        matrix.mapPoints(endPoint1);
+        matrix.mapPoints(startPoint2);
+        matrix.mapPoints(endPoint2);
+
+        Path path1 = new Path();
+        path1.moveTo(startPoint1[0], startPoint1[1]);
+        path1.lineTo(endPoint1[0], endPoint1[1]);
+        Path path2 = new Path();
+        path2.moveTo(startPoint2[0], startPoint2[1]);
+        path2.lineTo(endPoint2[0], endPoint2[1]);
+
+        return new GestureDescription(Arrays.asList(
+                new StrokeDescription(path1, 0, duration),
+                new StrokeDescription(path2, 0, duration)));
+    }
+
+    private GestureDescription() {}
+
+    private GestureDescription(List<StrokeDescription> strokes) {
+        mStrokes.addAll(strokes);
+    }
+
+    private GestureDescription(StrokeDescription stroke) {
+        mStrokes.add(stroke);
+    }
+
+    /**
+     * Get the number of stroke in the gesture.
+     *
+     * @return the number of strokes in this gesture
+     */
+    public int getStrokeCount() {
+        return mStrokes.size();
+    }
+
+    /**
+     * Read a stroke from the gesture
+     *
+     * @param index the index of the stroke
+     *
+     * @return A description of the stroke.
+     */
+    public StrokeDescription getStroke(@IntRange(from = 0) int index) {
+        return mStrokes.get(index);
+    }
+
+    /**
+     * Return the smallest key point (where a path starts or ends) that is at least a specified
+     * offset
+     * @param offset the minimum start time
+     * @return The next key time that is at least the offset or -1 if one can't be found
+     */
+    private long getNextKeyPointAtLeast(long offset) {
+        long nextKeyPoint = Long.MAX_VALUE;
+        for (int i = 0; i < mStrokes.size(); i++) {
+            long thisStartTime = mStrokes.get(i).mStartTime;
+            if ((thisStartTime < nextKeyPoint) && (thisStartTime >= offset)) {
+                nextKeyPoint = thisStartTime;
+            }
+            long thisEndTime = mStrokes.get(i).mEndTime;
+            if ((thisEndTime < nextKeyPoint) && (thisEndTime >= offset)) {
+                nextKeyPoint = thisEndTime;
+            }
+        }
+        return (nextKeyPoint == Long.MAX_VALUE) ? -1L : nextKeyPoint;
+    }
+
+    /**
+     * Get the points that correspond to a particular moment in time.
+     * @param time The time of interest
+     * @param touchPoints An array to hold the current touch points. Must be preallocated to at
+     * least the number of paths in the gesture to prevent going out of bounds
+     * @return The number of points found, and thus the number of elements set in each array
+     */
+    private int getPointsForTime(long time, TouchPoint[] touchPoints) {
+        int numPointsFound = 0;
+        for (int i = 0; i < mStrokes.size(); i++) {
+            StrokeDescription strokeDescription = mStrokes.get(i);
+            if (strokeDescription.hasPointForTime(time)) {
+                touchPoints[numPointsFound].mPathIndex = i;
+                touchPoints[numPointsFound].mIsStartOfPath = (time == strokeDescription.mStartTime);
+                touchPoints[numPointsFound].mIsEndOfPath = (time == strokeDescription.mEndTime);
+                strokeDescription.getPosForTime(time, mTempPos);
+                touchPoints[numPointsFound].mX = Math.round(mTempPos[0]);
+                touchPoints[numPointsFound].mY = Math.round(mTempPos[1]);
+                numPointsFound++;
+            }
+        }
+        return numPointsFound;
+    }
+
+    // Total duration assumes that the gesture starts at 0; waiting around to start a gesture
+    // counts against total duration
+    private static long getTotalDuration(List<StrokeDescription> paths) {
+        long latestEnd = Long.MIN_VALUE;
+        for (int i = 0; i < paths.size(); i++) {
+            StrokeDescription path = paths.get(i);
+            latestEnd = Math.max(latestEnd, path.mEndTime);
+        }
+        return Math.max(latestEnd, 0);
+    }
+
+    /**
+     * Builder for a {@code GestureDescription}
+     */
+    public static class Builder {
+
+        private final List<StrokeDescription> mStrokes = new ArrayList<>();
+
+        /**
+         * Add a stroke to the gesture description. Up to {@code MAX_STROKE_COUNT} paths may be
+         * added to a gesture, and the total gesture duration (earliest path start time to latest path
+         * end time) may not exceed {@code MAX_GESTURE_DURATION_MS}.
+         *
+         * @param strokeDescription the stroke to add.
+         *
+         * @return this
+         */
+        public Builder addStroke(@NonNull StrokeDescription strokeDescription) {
+            if (mStrokes.size() >= MAX_STROKE_COUNT) {
+                throw new RuntimeException("Attempting to add too many strokes to a gesture");
+            }
+
+            mStrokes.add(strokeDescription);
+
+            if (getTotalDuration(mStrokes) > MAX_GESTURE_DURATION_MS) {
+                mStrokes.remove(strokeDescription);
+                throw new RuntimeException("Gesture would exceed maximum duration with new stroke");
+            }
+            return this;
+        }
+
+        public GestureDescription build() {
+            if (mStrokes.size() == 0) {
+                throw new RuntimeException("Gestures must have at least one stroke");
+            }
+            return new GestureDescription(mStrokes);
+        }
+    }
+
+    /**
+     * Immutable description of stroke that can be part of a gesture.
+     */
+    public static class StrokeDescription {
+        Path mPath;
+        long mStartTime;
+        long mEndTime;
+        private float mTimeToLengthConversion;
+        private PathMeasure mPathMeasure;
+
+        /**
+         * @param path The path to follow. Must have exactly one contour, and that contour must
+         * have nonzero length. The bounds of the path must not be negative.
+         * @param startTime The time, in milliseconds, from the time the gesture starts to the
+         * time the stroke should start. Must not be negative.
+         * @param duration The duration, in milliseconds, the stroke takes to traverse the path.
+         * Must not be negative.
+         */
+        public StrokeDescription(@NonNull Path path,
+                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long startTime,
+                @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) {
+            if (duration <= 0) {
+                throw new IllegalArgumentException("Duration must be positive");
+            }
+            if (startTime < 0) {
+                throw new IllegalArgumentException("Start time must not be negative");
+            }
+            RectF bounds = new RectF();
+            path.computeBounds(bounds, false /* unused */);
+            if ((bounds.bottom < 0) || (bounds.top < 0) || (bounds.right < 0)
+                    || (bounds.left < 0)) {
+                throw new IllegalArgumentException("Path bounds must not be negative");
+            }
+            mPath = new Path(path);
+            mPathMeasure = new PathMeasure(path, false);
+            if (mPathMeasure.getLength() == 0) {
+                throw new IllegalArgumentException("Path has zero length");
+            }
+            if (mPathMeasure.nextContour()) {
+                throw new IllegalArgumentException("Path has more than one contour");
+            }
+            /*
+             * Calling nextContour has moved mPathMeasure off the first contour, which is the only
+             * one we care about. Set the path again to go back to the first contour.
+             */
+            mPathMeasure.setPath(path, false);
+            mStartTime = startTime;
+            mEndTime = startTime + duration;
+            if (duration > 0) {
+                mTimeToLengthConversion = getLength() / duration;
+            }
+        }
+
+        /**
+         * Retrieve a copy of the path for this stroke
+         *
+         * @return A copy of the path
+         */
+        public Path getPath() {
+            return new Path(mPath);
+        }
+
+        /**
+         * Get the stroke's start time
+         *
+         * @return the start time for this stroke.
+         */
+        public long getStartTime() {
+            return mStartTime;
+        }
+
+        /**
+         * Get the stroke's duration
+         *
+         * @return the duration for this stroke
+         */
+        public long getDuration() {
+            return mEndTime - mStartTime;
+        }
+
+        float getLength() {
+            return mPathMeasure.getLength();
+        }
+
+        /* Assumes hasPointForTime returns true */
+        boolean getPosForTime(long time, float[] pos) {
+            if (time == mEndTime) {
+                // Close to the end time, roundoff can be a problem
+                return mPathMeasure.getPosTan(getLength(), pos, null);
+            }
+            float length = mTimeToLengthConversion * ((float) (time - mStartTime));
+            return mPathMeasure.getPosTan(length, pos, null);
+        }
+
+        boolean hasPointForTime(long time) {
+            return ((time >= mStartTime) && (time <= mEndTime));
+        }
+    }
+
+    private static class TouchPoint {
+        int mPathIndex;
+        boolean mIsStartOfPath;
+        boolean mIsEndOfPath;
+        float mX;
+        float mY;
+
+        void copyFrom(TouchPoint other) {
+            mPathIndex = other.mPathIndex;
+            mIsStartOfPath = other.mIsStartOfPath;
+            mIsEndOfPath = other.mIsEndOfPath;
+            mX = other.mX;
+            mY = other.mY;
+        }
+    }
+
+    /**
+     * Class to convert a GestureDescription to a series of MotionEvents.
+     */
+    static class MotionEventGenerator {
+        /**
+         * Constants used to initialize all MotionEvents
+         */
+        private static final int EVENT_META_STATE = 0;
+        private static final int EVENT_BUTTON_STATE = 0;
+        private static final int EVENT_DEVICE_ID = 0;
+        private static final int EVENT_EDGE_FLAGS = 0;
+        private static final int EVENT_SOURCE = InputDevice.SOURCE_TOUCHSCREEN;
+        private static final int EVENT_FLAGS = 0;
+        private static final float EVENT_X_PRECISION = 1;
+        private static final float EVENT_Y_PRECISION = 1;
+
+        /* Lazily-created scratch memory for processing touches */
+        private static TouchPoint[] sCurrentTouchPoints;
+        private static TouchPoint[] sLastTouchPoints;
+        private static PointerCoords[] sPointerCoords;
+        private static PointerProperties[] sPointerProps;
+
+        static List<MotionEvent> getMotionEventsFromGestureDescription(
+                GestureDescription description, int sampleTimeMs) {
+            final List<MotionEvent> motionEvents = new ArrayList<>();
+
+            // Point data at each time we generate an event for
+            final TouchPoint[] currentTouchPoints =
+                    getCurrentTouchPoints(description.getStrokeCount());
+            // Point data sent in last touch event
+            int lastTouchPointSize = 0;
+            final TouchPoint[] lastTouchPoints =
+                    getLastTouchPoints(description.getStrokeCount());
+
+            /* Loop through each time slice where there are touch points */
+            long timeSinceGestureStart = 0;
+            long nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart);
+            while (nextKeyPointTime >= 0) {
+                timeSinceGestureStart = (lastTouchPointSize == 0) ? nextKeyPointTime
+                        : Math.min(nextKeyPointTime, timeSinceGestureStart + sampleTimeMs);
+                int currentTouchPointSize = description.getPointsForTime(timeSinceGestureStart,
+                        currentTouchPoints);
+
+                appendMoveEventIfNeeded(motionEvents, lastTouchPoints, lastTouchPointSize,
+                        currentTouchPoints, currentTouchPointSize, timeSinceGestureStart);
+                lastTouchPointSize = appendUpEvents(motionEvents, lastTouchPoints,
+                        lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
+                        timeSinceGestureStart);
+                lastTouchPointSize = appendDownEvents(motionEvents, lastTouchPoints,
+                        lastTouchPointSize, currentTouchPoints, currentTouchPointSize,
+                        timeSinceGestureStart);
+
+                /* Move to next time slice */
+                nextKeyPointTime = description.getNextKeyPointAtLeast(timeSinceGestureStart + 1);
+            }
+            return motionEvents;
+        }
+
+        private static TouchPoint[] getCurrentTouchPoints(int requiredCapacity) {
+            if ((sCurrentTouchPoints == null) || (sCurrentTouchPoints.length < requiredCapacity)) {
+                sCurrentTouchPoints = new TouchPoint[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sCurrentTouchPoints[i] = new TouchPoint();
+                }
+            }
+            return sCurrentTouchPoints;
+        }
+
+        private static TouchPoint[] getLastTouchPoints(int requiredCapacity) {
+            if ((sLastTouchPoints == null) || (sLastTouchPoints.length < requiredCapacity)) {
+                sLastTouchPoints = new TouchPoint[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sLastTouchPoints[i] = new TouchPoint();
+                }
+            }
+            return sLastTouchPoints;
+        }
+
+        private static PointerCoords[] getPointerCoords(int requiredCapacity) {
+            if ((sPointerCoords == null) || (sPointerCoords.length < requiredCapacity)) {
+                sPointerCoords = new PointerCoords[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sPointerCoords[i] = new PointerCoords();
+                }
+            }
+            return sPointerCoords;
+        }
+
+        private static PointerProperties[] getPointerProps(int requiredCapacity) {
+            if ((sPointerProps == null) || (sPointerProps.length < requiredCapacity)) {
+                sPointerProps = new PointerProperties[requiredCapacity];
+                for (int i = 0; i < requiredCapacity; i++) {
+                    sPointerProps[i] = new PointerProperties();
+                }
+            }
+            return sPointerProps;
+        }
+
+        private static void appendMoveEventIfNeeded(List<MotionEvent> motionEvents,
+                TouchPoint[] lastTouchPoints, int lastTouchPointsSize,
+                TouchPoint[] currentTouchPoints, int currentTouchPointsSize, long currentTime) {
+            /* Look for pointers that have moved */
+            boolean moveFound = false;
+            for (int i = 0; i < currentTouchPointsSize; i++) {
+                int lastPointsIndex = findPointByPathIndex(lastTouchPoints, lastTouchPointsSize,
+                        currentTouchPoints[i].mPathIndex);
+                if (lastPointsIndex >= 0) {
+                    moveFound |= (lastTouchPoints[lastPointsIndex].mX != currentTouchPoints[i].mX)
+                            || (lastTouchPoints[lastPointsIndex].mY != currentTouchPoints[i].mY);
+                    lastTouchPoints[lastPointsIndex].copyFrom(currentTouchPoints[i]);
+                }
+            }
+
+            if (moveFound) {
+                long downTime = motionEvents.get(motionEvents.size() - 1).getDownTime();
+                motionEvents.add(obtainMotionEvent(downTime, currentTime, MotionEvent.ACTION_MOVE,
+                        lastTouchPoints, lastTouchPointsSize));
+            }
+        }
+
+        private static int appendUpEvents(List<MotionEvent> motionEvents,
+                TouchPoint[] lastTouchPoints, int lastTouchPointsSize,
+                TouchPoint[] currentTouchPoints, int currentTouchPointsSize, long currentTime) {
+            /* Look for a pointer at the end of its path */
+            for (int i = 0; i < currentTouchPointsSize; i++) {
+                if (currentTouchPoints[i].mIsEndOfPath) {
+                    int indexOfUpEvent = findPointByPathIndex(lastTouchPoints, lastTouchPointsSize,
+                            currentTouchPoints[i].mPathIndex);
+                    if (indexOfUpEvent < 0) {
+                        continue; // Should not happen
+                    }
+                    long downTime = motionEvents.get(motionEvents.size() - 1).getDownTime();
+                    int action = (lastTouchPointsSize == 1) ? MotionEvent.ACTION_UP
+                            : MotionEvent.ACTION_POINTER_UP;
+                    action |= indexOfUpEvent << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+                    motionEvents.add(obtainMotionEvent(downTime, currentTime, action,
+                            lastTouchPoints, lastTouchPointsSize));
+                    /* Remove this point from lastTouchPoints */
+                    for (int j = indexOfUpEvent; j < lastTouchPointsSize - 1; j++) {
+                        lastTouchPoints[j].copyFrom(lastTouchPoints[j+1]);
+                    }
+                    lastTouchPointsSize--;
+                }
+            }
+            return lastTouchPointsSize;
+        }
+
+        private static int appendDownEvents(List<MotionEvent> motionEvents,
+                TouchPoint[] lastTouchPoints, int lastTouchPointsSize,
+                TouchPoint[] currentTouchPoints, int currentTouchPointsSize, long currentTime) {
+            /* Look for a pointer that is just starting */
+            for (int i = 0; i < currentTouchPointsSize; i++) {
+                if (currentTouchPoints[i].mIsStartOfPath) {
+                    /* Add the point to last coords and use the new array to generate the event */
+                    lastTouchPoints[lastTouchPointsSize++].copyFrom(currentTouchPoints[i]);
+                    int action = (lastTouchPointsSize == 1) ? MotionEvent.ACTION_DOWN
+                            : MotionEvent.ACTION_POINTER_DOWN;
+                    long downTime = (action == MotionEvent.ACTION_DOWN) ? currentTime :
+                            motionEvents.get(motionEvents.size() - 1).getDownTime();
+                    action |= i << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+                    motionEvents.add(obtainMotionEvent(downTime, currentTime, action,
+                            lastTouchPoints, lastTouchPointsSize));
+                }
+            }
+            return lastTouchPointsSize;
+        }
+
+        private static MotionEvent obtainMotionEvent(long downTime, long eventTime, int action,
+                TouchPoint[] touchPoints, int touchPointsSize) {
+            PointerCoords[] pointerCoords = getPointerCoords(touchPointsSize);
+            PointerProperties[] pointerProperties = getPointerProps(touchPointsSize);
+            for (int i = 0; i < touchPointsSize; i++) {
+                pointerProperties[i].id = touchPoints[i].mPathIndex;
+                pointerProperties[i].toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+                pointerCoords[i].clear();
+                pointerCoords[i].pressure = 1.0f;
+                pointerCoords[i].size = 1.0f;
+                pointerCoords[i].x = touchPoints[i].mX;
+                pointerCoords[i].y = touchPoints[i].mY;
+            }
+            return MotionEvent.obtain(downTime, eventTime, action, touchPointsSize,
+                    pointerProperties, pointerCoords, EVENT_META_STATE, EVENT_BUTTON_STATE,
+                    EVENT_X_PRECISION, EVENT_Y_PRECISION, EVENT_DEVICE_ID, EVENT_EDGE_FLAGS,
+                    EVENT_SOURCE, EVENT_FLAGS);
+        }
+
+        private static int findPointByPathIndex(TouchPoint[] touchPoints, int touchPointsSize,
+                int pathIndex) {
+            for (int i = 0; i < touchPointsSize; i++) {
+                if (touchPoints[i].mPathIndex == pathIndex) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+    }
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 15666bf..6280542 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -42,4 +42,6 @@
     void onKeyEvent(in KeyEvent event, int sequence);
 
     void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
+
+    void onPerformGestureResult(int sequence, boolean completedSuccessfully);
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 6ac50bd..a65b87b 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -16,10 +16,12 @@
 
 package android.accessibilityservice;
 
-import android.os.Bundle;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.pm.ParceledListSlice;
 import android.graphics.Region;
+import android.os.Bundle;
 import android.view.MagnificationSpec;
+import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.accessibility.AccessibilityWindowInfo;
@@ -79,4 +81,6 @@
         boolean animate);
 
     void setMagnificationCallbackEnabled(boolean enabled);
+
+    void sendMotionEvents(int sequence, in ParceledListSlice events);
 }
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index a312e3f..690e674 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.Manifest;
+import android.annotation.SystemApi;
 import android.util.Log;
 
 import java.util.Arrays;
@@ -762,7 +763,9 @@
      * @throws NetworkErrorException if the authenticator could not honor the
      *             request due to a network error
      * @see #finishSession(AccountAuthenticatorResponse, String, Bundle)
+     * @hide
      */
+    @SystemApi
     public Bundle startAddAccountSession(
             final AccountAuthenticatorResponse response,
             final String accountType,
@@ -818,7 +821,9 @@
      * @throws NetworkErrorException if the authenticator could not honor the
      *             request due to a network error
      * @see #finishSession(AccountAuthenticatorResponse, String, Bundle)
+     * @hide
      */
+    @SystemApi
     public Bundle startUpdateCredentialsSession(
             final AccountAuthenticatorResponse response,
             final Account account,
@@ -870,7 +875,9 @@
      *         </ul>
      * @throws NetworkErrorException
      * @see #startAddAccountSession and #startUpdateCredentialsSession
+     * @hide
      */
+    @SystemApi
     public Bundle finishSession(
             final AccountAuthenticatorResponse response,
             final String accountType,
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index ada1ac2..10f5d0d 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.Size;
+import android.annotation.SystemApi;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -244,14 +245,18 @@
      * Bundle key used for a {@link Bundle} in result from
      * {@link #startAddAccountSession} and friends which returns session data
      * for installing an account later.
+     * @hide
      */
+    @SystemApi
     public static final String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
 
     /**
      * Bundle key used for the {@link String} account status token in result
      * from {@link #startAddAccountSession} and friends which returns
      * information about a particular account.
+     * @hide
      */
+    @SystemApi
     public static final String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
 
     public static final String ACTION_AUTHENTICATOR_INTENT =
@@ -1848,6 +1853,22 @@
         }.start();
     }
 
+    /**
+     * @hide
+     * Checks if the given account exists on any of the users on the device.
+     * Only the system process can call this method.
+     *
+     * @param account The account to check for existence.
+     * @return whether any user has this account
+     */
+    public boolean someUserHasAccount(@NonNull final Account account) {
+        try {
+            return mService.someUserHasAccount(account);
+        } catch (RemoteException re) {
+            throw new RuntimeException(re);
+        }
+    }
+
     private void ensureNotOnMainThread() {
         final Looper looper = Looper.myLooper();
         if (looper != null && looper == mContext.getMainLooper()) {
@@ -2667,7 +2688,9 @@
      *         trouble
      *         </ul>
      * @see #finishSession
+     * @hide
      */
+    @SystemApi
     public AccountManagerFuture<Bundle> startAddAccountSession(
             final String accountType,
             final String authTokenType,
@@ -2749,7 +2772,9 @@
      *         trouble
      *         </ul>
      * @see #finishSession
+     * @hide
      */
+    @SystemApi
     public AccountManagerFuture<Bundle> startUpdateCredentialsSession(
             final Account account,
             final String authTokenType,
@@ -2818,7 +2843,9 @@
      *         trouble
      *         </ul>
      * @see #startAddAccountSession and #startUpdateCredentialsSession
+     * @hide
      */
+    @SystemApi
     public AccountManagerFuture<Bundle> finishSession(
             final Bundle sessionBundle,
             final Activity activity,
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 4af9f33..608501f 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -95,4 +95,7 @@
     /* Finish session started by startAddAccountSession(...) or startUpdateCredentialsSession(...) */
     void finishSession(in IAccountManagerResponse response, in Bundle sessionBundle,
         boolean expectActivityLaunch, in Bundle appInfo);
+
+    /* Check if an account exists on any user on the device. */
+    boolean someUserHasAccount(in Account account);
 }
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 8928e99..e993cca 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -861,22 +861,23 @@
         if (mProperty != null) {
             Object value = convertBack(mProperty.get(target));
             kf.setValue(value);
-        }
-        try {
-            if (mGetter == null) {
-                Class targetClass = target.getClass();
-                setupGetter(targetClass);
+        } else {
+            try {
                 if (mGetter == null) {
-                    // Already logged the error - just return to avoid NPE
-                    return;
+                    Class targetClass = target.getClass();
+                    setupGetter(targetClass);
+                    if (mGetter == null) {
+                        // Already logged the error - just return to avoid NPE
+                        return;
+                    }
                 }
+                Object value = convertBack(mGetter.invoke(target));
+                kf.setValue(value);
+            } catch (InvocationTargetException e) {
+                Log.e("PropertyValuesHolder", e.toString());
+            } catch (IllegalAccessException e) {
+                Log.e("PropertyValuesHolder", e.toString());
             }
-            Object value = convertBack(mGetter.invoke(target));
-            kf.setValue(value);
-        } catch (InvocationTargetException e) {
-            Log.e("PropertyValuesHolder", e.toString());
-        } catch (IllegalAccessException e) {
-            Log.e("PropertyValuesHolder", e.toString());
         }
     }
 
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 7f9a5d3..e721de9 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,6 +17,7 @@
 package android.animation;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
 import android.os.Looper;
 import android.os.Trace;
 import android.util.AndroidRuntimeException;
@@ -25,6 +26,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.LinearInterpolator;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -234,6 +237,11 @@
      * Public constants
      */
 
+    /** @hide */
+    @IntDef({RESTART, REVERSE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RepeatMode {}
+
     /**
      * When the animation reaches the end and <code>repeatCount</code> is INFINITE
      * or a positive value, the animation restarts from the beginning.
@@ -807,7 +815,7 @@
      *
      * @param value {@link #RESTART} or {@link #REVERSE}
      */
-    public void setRepeatMode(int value) {
+    public void setRepeatMode(@RepeatMode int value) {
         mRepeatMode = value;
     }
 
@@ -816,6 +824,7 @@
      *
      * @return either one of {@link #REVERSE} or {@link #RESTART}
      */
+    @RepeatMode
     public int getRepeatMode() {
         return mRepeatMode;
     }
diff --git a/core/java/android/annotation/AppIdInt.java b/core/java/android/annotation/AppIdInt.java
new file mode 100644
index 0000000..29838dd
--- /dev/null
+++ b/core/java/android/annotation/AppIdInt.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element is a multi-user application ID. This is
+ * <em>not</em> the same as a UID.
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface AppIdInt {
+}
diff --git a/core/java/android/annotation/IntDef.java b/core/java/android/annotation/IntDef.java
index a18bfa5..434a9c7 100644
--- a/core/java/android/annotation/IntDef.java
+++ b/core/java/android/annotation/IntDef.java
@@ -42,7 +42,7 @@
  * For a flag, set the flag attribute:
  * <pre><code>
  *  &#64;IntDef(
- *      flag = true
+ *      flag = true,
  *      value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
  * </code></pre>
  *
diff --git a/core/java/android/annotation/UserIdInt.java b/core/java/android/annotation/UserIdInt.java
new file mode 100644
index 0000000..7b9ce25
--- /dev/null
+++ b/core/java/android/annotation/UserIdInt.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element is a multi-user user ID. This is
+ * <em>not</em> the same as a UID.
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface UserIdInt {
+}
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 3e6b595..6fc0d74 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -1082,6 +1082,10 @@
         return false;
     }
 
+    /** @hide */
+    public void onDestroy() {
+    }
+
     /**
      * Common implementation for requestFocus that takes in the Toolbar and moves focus
      * to the contents. This makes the ViewGroups containing the toolbar allow focus while it stays
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 800ebc6..e312596 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -62,7 +62,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
-import android.graphics.Rect;
+import android.graphics.drawable.Icon;
 import android.media.AudioManager;
 import android.media.session.MediaController;
 import android.net.Uri;
@@ -72,6 +72,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.UserHandle;
@@ -79,23 +80,28 @@
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SuperNotCalledException;
 import android.view.ActionMode;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
 import android.view.SearchEvent;
 import android.view.View;
 import android.view.View.OnCreateContextMenuListener;
@@ -104,10 +110,17 @@
 import android.view.ViewManager;
 import android.view.ViewRootImpl;
 import android.view.Window;
+import android.view.Window.WindowControllerCallback;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
+import android.widget.Toolbar;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.PhoneWindow;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -117,6 +130,8 @@
 import java.util.HashMap;
 import java.util.List;
 
+import static java.lang.Character.MIN_VALUE;
+
 /**
  * An activity is a single, focused thing that the user can do.  Almost all
  * activities interact with the user, so the Activity class takes care of
@@ -1595,6 +1610,30 @@
     public void onProvideAssistContent(AssistContent outContent) {
     }
 
+    @Override
+    public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+        if (menu == null) {
+          return;
+        }
+        KeyboardShortcutGroup group = null;
+        int menuSize = menu.size();
+        for (int i = 0; i < menuSize; ++i) {
+            final MenuItem item = menu.getItem(i);
+            final CharSequence title = item.getTitle();
+            final char alphaShortcut = item.getAlphabeticShortcut();
+            if (title != null && alphaShortcut != MIN_VALUE) {
+                if (group == null) {
+                    group = new KeyboardShortcutGroup(null /* no label */);
+                }
+                group.addItem(new KeyboardShortcutInfo(
+                    title, alphaShortcut, KeyEvent.META_CTRL_ON));
+            }
+        }
+        if (group != null) {
+            data.add(group);
+        }
+    }
+
     /**
      * Ask to have the current assistant shown to the user.  This only works if the calling
      * activity is the current foreground activity.  It is the same as calling
@@ -1703,6 +1742,10 @@
             mSearchManager.stopSearch();
         }
 
+        if (mActionBar != null) {
+            mActionBar.onDestroy();
+        }
+
         getApplication().dispatchActivityDestroyed(this);
     }
 
@@ -2208,14 +2251,22 @@
      * @param toolbar Toolbar to set as the Activity's action bar
      */
     public void setActionBar(@Nullable Toolbar toolbar) {
-        if (getActionBar() instanceof WindowDecorActionBar) {
+        final ActionBar ab = getActionBar();
+        if (ab instanceof WindowDecorActionBar) {
             throw new IllegalStateException("This Activity already has an action bar supplied " +
                     "by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " +
                     "android:windowActionBar to false in your theme to use a Toolbar instead.");
         }
-        // Clear out the MenuInflater to make sure that it is valid for the new Action Bar
+
+        // If we reach here then we're setting a new action bar
+        // First clear out the MenuInflater to make sure that it is valid for the new Action Bar
         mMenuInflater = null;
 
+        // If we have an action bar currently, destroy it
+        if (ab != null) {
+            ab.onDestroy();
+        }
+
         ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(), this);
         mActionBar = tbab;
         mWindow.setCallback(tbab.getWrappedWindowCallback());
@@ -2795,17 +2846,15 @@
 
 
     /**
-     * Called to move the window and its activity/task to a different stack container.
-     * For example, a window can move between
-     * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack and
-     * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} stack.
+     * Moves the activity from
+     * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} to
+     * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack.
      *
-     * @param stackId stack Id to change to.
      * @hide
      */
     @Override
-    public void changeWindowStack(int stackId) throws RemoteException {
-        ActivityManagerNative.getDefault().moveActivityToStack(mToken, stackId);
+    public void exitFreeformMode() throws RemoteException {
+        ActivityManagerNative.getDefault().exitFreeformMode(mToken);
     }
 
     /** Returns the current stack Id for the window.
@@ -2834,16 +2883,15 @@
         if (keyCode == KeyEvent.KEYCODE_MENU &&
                 mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
             return true;
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
-            // Capture the Alt-up and send focus to the ActionBar
+        } else if (event.isCtrlPressed() &&
+                event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') {
+            // Capture the Control-< and send focus to the ActionBar
             final int action = event.getAction();
             if (action == KeyEvent.ACTION_DOWN) {
-                if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                    final ActionBar actionBar = getActionBar();
-                    if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
-                        mEatKeyUpEvent = true;
-                        return true;
-                    }
+                final ActionBar actionBar = getActionBar();
+                if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
+                    mEatKeyUpEvent = true;
+                    return true;
                 }
             } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
                 mEatKeyUpEvent = false;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8637dde..2175a9e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -36,10 +37,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.UriPermission;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -59,12 +62,15 @@
 import android.util.DisplayMetrics;
 import android.util.Size;
 import android.util.Slog;
+
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -79,6 +85,36 @@
     private final Context mContext;
     private final Handler mHandler;
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            BUGREPORT_OPTION_FULL,
+            BUGREPORT_OPTION_INTERACTIVE,
+            BUGREPORT_OPTION_REMOTE
+    })
+    /**
+     * Defines acceptable types of bugreports.
+     * @hide
+     */
+    public @interface BugreportMode {}
+    /**
+     * Takes a bugreport without user interference (and hence causing less
+     * interference to the system), but includes all sections.
+     * @hide
+     */
+    public static final int BUGREPORT_OPTION_FULL = 0;
+    /**
+     * Allows user to monitor progress and enter additional data; might not include all
+     * sections.
+     * @hide
+     */
+    public static final int BUGREPORT_OPTION_INTERACTIVE = 1;
+    /**
+     * Takes a bugreport requested remotely by administrator of the Device Owner app,
+     * not the device's user.
+     * @hide
+     */
+    public static final int BUGREPORT_OPTION_REMOTE = 2;
+
     /**
      * <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code
      * <meta-data>}</a> name for a 'home' Activity that declares a package that is to be
@@ -556,6 +592,14 @@
         public static boolean isAlwaysOnTop(int stackId) {
             return stackId == PINNED_STACK_ID;
         }
+
+        /**
+         * Returns true if the top task in the task is allowed to return home when finished and
+         * there are other tasks in the stack.
+         */
+        public static boolean allowTopTaskToReturnHome(int stackId) {
+            return stackId != PINNED_STACK_ID;
+        }
     }
 
     /**
@@ -2256,6 +2300,45 @@
         return clearApplicationUserData(mContext.getPackageName(), null);
     }
 
+
+    /**
+     * Permits an application to get the persistent URI permissions granted to another.
+     *
+     * <p>Typically called by Settings.
+     *
+     * @param packageName application to look for the granted permissions
+     * @return list of granted URI permissions
+     *
+     * @hide
+     */
+    public ParceledListSlice<UriPermission> getGrantedUriPermissions(String packageName) {
+        try {
+            return ActivityManagerNative.getDefault().getGrantedUriPermissions(packageName,
+                    UserHandle.myUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't get granted URI permissions for :" + packageName, e);
+            return ParceledListSlice.emptyList();
+        }
+    }
+
+    /**
+     * Permits an application to clear the persistent URI permissions granted to another.
+     *
+     * <p>Typically called by Settings.
+     *
+     * @param packageName application to clear its granted permissions
+     *
+     * @hide
+     */
+    public void clearGrantedUriPermissions(String packageName) {
+        try {
+            ActivityManagerNative.getDefault().clearGrantedUriPermissions(packageName,
+                    UserHandle.myUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't clear granted URI permissions for :" + packageName, e);
+        }
+    }
+
     /**
      * Information you can retrieve about any processes that are in an error condition.
      */
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 3a3aa92..63b6825 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -332,10 +332,9 @@
         {
             data.enforceInterface(IActivityManager.descriptor);
             final int taskId = data.readInt();
-            final int launchStackId = data.readInt();
             final Bundle options =
                     data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
-            final int result = startActivityFromRecents(taskId, launchStackId, options);
+            final int result = startActivityFromRecents(taskId, options);
             reply.writeNoException();
             reply.writeInt(result);
             return true;
@@ -571,6 +570,14 @@
             return true;
         }
 
+        case ACTIVITY_RELAUNCHED_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            activityRelaunched(token);
+            reply.writeNoException();
+            return true;
+        }
+
         case GET_CALLING_PACKAGE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
@@ -786,6 +793,39 @@
             return true;
         }
 
+        case RESIZE_DOCKED_STACK_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final boolean hasBounds = data.readInt() != 0;
+            Rect bounds = null;
+            if (hasBounds) {
+                bounds = Rect.CREATOR.createFromParcel(data);
+            }
+            final boolean hasTempDockedTaskBounds = data.readInt() != 0;
+            Rect tempDockedTaskBounds = null;
+            if (hasTempDockedTaskBounds) {
+                tempDockedTaskBounds = Rect.CREATOR.createFromParcel(data);
+            }
+            final boolean hasTempDockedTaskInsetBounds = data.readInt() != 0;
+            Rect tempDockedTaskInsetBounds = null;
+            if (hasTempDockedTaskInsetBounds) {
+                tempDockedTaskInsetBounds = Rect.CREATOR.createFromParcel(data);
+            }
+            final boolean hasTempOtherTaskBounds = data.readInt() != 0;
+            Rect tempOtherTaskBounds = null;
+            if (hasTempOtherTaskBounds) {
+                tempOtherTaskBounds = Rect.CREATOR.createFromParcel(data);
+            }
+            final boolean hasTempOtherTaskInsetBounds = data.readInt() != 0;
+            Rect tempOtherTaskInsetBounds = null;
+            if (hasTempOtherTaskInsetBounds) {
+                tempOtherTaskInsetBounds = Rect.CREATOR.createFromParcel(data);
+            }
+            resizeDockedStack(bounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+                    tempOtherTaskBounds, tempOtherTaskInsetBounds);
+            reply.writeNoException();
+            return true;
+        }
+
         case POSITION_TASK_IN_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int taskId = data.readInt();
@@ -1368,6 +1408,26 @@
             return true;
         }
 
+        case GET_GRANTED_URI_PERMISSIONS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final String packageName = data.readString();
+            final int userId = data.readInt();
+            final ParceledListSlice<UriPermission> perms = getGrantedUriPermissions(packageName,
+                    userId);
+            reply.writeNoException();
+            perms.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            return true;
+        }
+
+        case CLEAR_GRANTED_URI_PERMISSIONS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final String packageName = data.readString();
+            final int userId = data.readInt();
+            clearGrantedUriPermissions(packageName, userId);
+            reply.writeNoException();
+            return true;
+        }
+
         case SHOW_WAITING_FOR_DEBUGGER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder b = data.readStrongBinder();
@@ -2246,8 +2306,8 @@
 
         case REQUEST_BUG_REPORT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            boolean progress = data.readInt() != 0;
-            requestBugReport(progress);
+            int bugreportType = data.readInt();
+            requestBugReport(bugreportType);
             reply.writeNoException();
             return true;
         }
@@ -2712,11 +2772,10 @@
             reply.writeInt(stackId);
             return true;
         }
-        case MOVE_ACTIVITY_TO_STACK_TRANSACTION: {
+        case EXIT_FREEFORM_MODE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            int stackId = data.readInt();
-            moveActivityToStack(token, stackId);
+            exitFreeformMode(token);
             reply.writeNoException();
             return true;
         }
@@ -2739,7 +2798,8 @@
         case MOVE_TASKS_TO_FULLSCREEN_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             final int stackId = data.readInt();
-            moveTasksToFullscreenStack(stackId);
+            final boolean onTop = data.readInt() == 1;
+            moveTasksToFullscreenStack(stackId, onTop);
             reply.writeNoException();
             return true;
         }
@@ -2783,6 +2843,14 @@
             reply.writeNoException();
             return true;
         }
+        case IS_APP_FOREGROUND_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final int userHandle = data.readInt();
+            final boolean isForeground = isAppForeground(userHandle);
+            reply.writeNoException();
+            reply.writeInt(isForeground ? 1 : 0);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3088,13 +3156,12 @@
         data.recycle();
         return result != 0;
     }
-    public int startActivityFromRecents(int taskId, int launchStackId, Bundle options)
+    public int startActivityFromRecents(int taskId, Bundle options)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(taskId);
-        data.writeInt(launchStackId);
         if (options == null) {
             data.writeInt(0);
         } else {
@@ -3382,6 +3449,17 @@
         data.recycle();
         reply.recycle();
     }
+    public void activityRelaunched(IBinder token) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(ACTIVITY_RELAUNCHED_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
     public String getCallingPackage(IBinder token) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -3692,6 +3770,50 @@
         reply.recycle();
     }
     @Override
+    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds)
+            throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        if (dockedBounds != null) {
+            data.writeInt(1);
+            dockedBounds.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        if (tempDockedTaskBounds != null) {
+            data.writeInt(1);
+            tempDockedTaskBounds.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        if (tempDockedTaskInsetBounds != null) {
+            data.writeInt(1);
+            tempDockedTaskInsetBounds.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        if (tempOtherTaskBounds != null) {
+            data.writeInt(1);
+            tempOtherTaskBounds.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        if (tempOtherTaskInsetBounds != null) {
+            data.writeInt(1);
+            tempOtherTaskInsetBounds.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(RESIZE_DOCKED_STACK_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+    @Override
     public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -4518,6 +4640,7 @@
         data.writeInt(incoming ? 1 : 0);
         mRemote.transact(GET_PERSISTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
         reply.readException();
+        @SuppressWarnings("unchecked")
         final ParceledListSlice<UriPermission> perms = ParceledListSlice.CREATOR.createFromParcel(
                 reply);
         data.recycle();
@@ -4525,6 +4648,37 @@
         return perms;
     }
 
+    @Override
+    public ParceledListSlice<UriPermission> getGrantedUriPermissions(String packageName, int userId)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeInt(userId);
+        mRemote.transact(GET_GRANTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        @SuppressWarnings("unchecked")
+        final ParceledListSlice<UriPermission> perms = ParceledListSlice.CREATOR.createFromParcel(
+                reply);
+        data.recycle();
+        reply.recycle();
+        return perms;
+    }
+
+    @Override
+    public void clearGrantedUriPermissions(String packageName, int userId) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeInt(userId);
+        mRemote.transact(CLEAR_GRANTED_URI_PERMISSIONS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -5675,11 +5829,12 @@
         reply.recycle();
     }
 
-    public void requestBugReport(boolean progress) throws RemoteException {
+    public void requestBugReport(@ActivityManager.BugreportMode int bugreportType)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeInt(progress ? 1 : 0);
+        data.writeInt(bugreportType);
         mRemote.transact(REQUEST_BUG_REPORT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -6363,13 +6518,12 @@
     }
 
     @Override
-    public void moveActivityToStack(IBinder token, int stackId) throws RemoteException {
+    public void exitFreeformMode(IBinder token) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        data.writeInt(stackId);
-        mRemote.transact(MOVE_ACTIVITY_TO_STACK_TRANSACTION, data, reply, 0);
+        mRemote.transact(EXIT_FREEFORM_MODE_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -6428,11 +6582,12 @@
     }
 
     @Override
-    public void moveTasksToFullscreenStack(int fromStackId) throws RemoteException {
+    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(fromStackId);
+        data.writeInt(onTop ? 1 : 0);
         mRemote.transact(MOVE_TASKS_TO_FULLSCREEN_STACK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
@@ -6494,5 +6649,18 @@
         reply.recycle();
     }
 
+    @Override
+    public boolean isAppForeground(int uid) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(uid);
+        mRemote.transact(IS_APP_FOREGROUND_TRANSACTION, data, reply, 0);
+        final boolean isForeground = reply.readInt() == 1 ? true : false;
+        data.recycle();
+        reply.recycle();
+        return isForeground;
+    };
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index cee1aa5..e596a68 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
 import android.content.Context;
 import android.content.Intent;
@@ -67,7 +68,8 @@
      * The bounds (window size) that the activity should be launched in. Set to null explicitly for
      * full screen. If the key is not found, previous bounds will be preserved.
      * NOTE: This value is ignored on devices that don't have
-     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} enabled.
+     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
+     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
      * @hide
      */
     public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
@@ -145,6 +147,12 @@
     private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
 
     /**
+     * The stack id the activity should be launched into.
+     * @hide
+     */
+    private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
+
+    /**
      * Where the docked stack should be positioned.
      * @hide
      */
@@ -215,6 +223,7 @@
     private int mResultCode;
     private int mExitCoordinatorIndex;
     private PendingIntent mUsageTimeReport;
+    private int mLaunchStackId = INVALID_STACK_ID;
     private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
     private AppTransitionAnimationSpec mAnimSpecs[];
 
@@ -754,6 +763,7 @@
                 mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
                 break;
         }
+        mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
         mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
         if (opts.containsKey(KEY_ANIM_SPECS)) {
             Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
@@ -901,7 +911,19 @@
     }
 
     /** @hide */
-    public int getDockCreateMode() { return mDockCreateMode; }
+    public int getLaunchStackId() {
+        return mLaunchStackId;
+    }
+
+    /** @hide */
+    public void setLaunchStackId(int launchStackId) {
+        mLaunchStackId = launchStackId;
+    }
+
+    /** @hide */
+    public int getDockCreateMode() {
+        return mDockCreateMode;
+    }
 
     /** @hide */
     public void setDockCreateMode(int dockCreateMode) {
@@ -1049,6 +1071,7 @@
                 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
                 break;
         }
+        b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
         b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
         if (mAnimSpecs != null) {
             b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ed168d1..2f2fbbe 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -101,6 +101,9 @@
 import android.view.WindowManagerGlobal;
 import android.renderscript.RenderScriptCacheDir;
 import android.security.keystore.AndroidKeyStoreProvider;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.ErrnoException;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IVoiceInteractor;
@@ -820,48 +823,6 @@
 
             setCoreSettings(coreSettings);
 
-            /*
-             * Two possible indications that this package could be
-             * sharing its runtime with other packages:
-             *
-             * 1.) the sharedUserId attribute is set in the manifest,
-             *     indicating a request to share a VM with other
-             *     packages with the same sharedUserId.
-             *
-             * 2.) the application element of the manifest has an
-             *     attribute specifying a non-default process name,
-             *     indicating the desire to run in another packages VM.
-             *
-             * If sharing is enabled we do not have a unique application
-             * in a process and therefore cannot rely on the package
-             * name inside the runtime.
-             */
-            IPackageManager pm = getPackageManager();
-            android.content.pm.PackageInfo pi = null;
-            try {
-                pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
-            } catch (RemoteException e) {
-            }
-            if (pi != null) {
-                boolean sharedUserIdSet = (pi.sharedUserId != null);
-                boolean processNameNotDefault =
-                (pi.applicationInfo != null &&
-                 !appInfo.packageName.equals(pi.applicationInfo.processName));
-                boolean sharable = (sharedUserIdSet || processNameNotDefault);
-
-                // Tell the VMRuntime about the application, unless it is shared
-                // inside a process.
-                if (!sharable) {
-                    final List<String> codePaths = new ArrayList<>();
-                    codePaths.add(appInfo.sourceDir);
-                    if (appInfo.splitSourceDirs != null) {
-                        Collections.addAll(codePaths, appInfo.splitSourceDirs);
-                    }
-                    VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
-                            codePaths.toArray(new String[codePaths.size()]));
-                }
-            }
-
             AppBindData data = new AppBindData();
             data.processName = processName;
             data.appInfo = appInfo;
@@ -1854,7 +1815,9 @@
         ApplicationInfo ai = null;
         try {
             ai = getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.GET_SHARED_LIBRARY_FILES, userId);
+                    PackageManager.GET_SHARED_LIBRARY_FILES
+                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                    userId);
         } catch (RemoteException e) {
             // Ignore
         }
@@ -2052,7 +2015,7 @@
     private static final String ONE_COUNT_COLUMN_HEADER = "%21s %8s";
 
     // Formatting for checkin service - update version if row format changes
-    private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
+    private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4;
 
     static void printRow(PrintWriter pw, String format, Object...objs) {
         pw.println(String.format(format, objs));
@@ -2128,6 +2091,25 @@
             pw.print(memInfo.otherPrivateClean); pw.print(',');
             pw.print(memInfo.getTotalPrivateClean()); pw.print(',');
 
+            // Heap info - swapped out
+            pw.print(memInfo.nativeSwappedOut); pw.print(',');
+            pw.print(memInfo.dalvikSwappedOut); pw.print(',');
+            pw.print(memInfo.otherSwappedOut); pw.print(',');
+            pw.print(memInfo.getTotalSwappedOut()); pw.print(',');
+
+            // Heap info - swapped out pss
+            if (memInfo.hasSwappedOutPss) {
+                pw.print(memInfo.nativeSwappedOutPss); pw.print(',');
+                pw.print(memInfo.dalvikSwappedOutPss); pw.print(',');
+                pw.print(memInfo.otherSwappedOutPss); pw.print(',');
+                pw.print(memInfo.getTotalSwappedOutPss()); pw.print(',');
+            } else {
+                pw.print("N/A,");
+                pw.print("N/A,");
+                pw.print("N/A,");
+                pw.print("N/A,");
+            }
+
             // Heap info - other areas
             for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
                 pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(',');
@@ -2137,6 +2119,12 @@
                 pw.print(memInfo.getOtherSharedClean(i)); pw.print(',');
                 pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(',');
                 pw.print(memInfo.getOtherPrivateClean(i)); pw.print(',');
+                pw.print(memInfo.getOtherSwappedOut(i)); pw.print(',');
+                if (memInfo.hasSwappedOutPss) {
+                    pw.print(memInfo.getOtherSwappedOutPss(i)); pw.print(',');
+                } else {
+                    pw.print("N/A,");
+                }
             }
             return;
         }
@@ -2144,35 +2132,44 @@
         if (!dumpSummaryOnly) {
             if (dumpFullInfo) {
                 printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private",
-                        "Shared", "Private", "Swapped", "Heap", "Heap", "Heap");
+                        "Shared", "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
+                        "Heap", "Heap", "Heap");
                 printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty",
-                        "Clean", "Clean", "Dirty", "Size", "Alloc", "Free");
+                        "Clean", "Clean", "Dirty",
+                        "Size", "Alloc", "Free");
                 printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------",
                         "------", "------", "------", "------", "------", "------");
                 printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss,
                         memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
                         memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
-                        memInfo.nativePrivateClean, memInfo.nativeSwappedOut,
+                        memInfo.nativePrivateClean, memInfo.hasSwappedOutPss ?
+                        memInfo.nativeSwappedOut : memInfo.nativeSwappedOutPss,
                         nativeMax, nativeAllocated, nativeFree);
                 printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
                         memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
                         memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
-                        memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut,
+                        memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss ?
+                        memInfo.dalvikSwappedOut : memInfo.dalvikSwappedOutPss,
                         dalvikMax, dalvikAllocated, dalvikFree);
             } else {
                 printRow(pw, HEAP_COLUMN, "", "Pss", "Private",
-                        "Private", "Swapped", "Heap", "Heap", "Heap");
+                        "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
+                        "Heap", "Heap", "Heap");
                 printRow(pw, HEAP_COLUMN, "", "Total", "Dirty",
                         "Clean", "Dirty", "Size", "Alloc", "Free");
                 printRow(pw, HEAP_COLUMN, "", "------", "------", "------",
                         "------", "------", "------", "------", "------");
                 printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss,
                         memInfo.nativePrivateDirty,
-                        memInfo.nativePrivateClean, memInfo.nativeSwappedOut,
+                        memInfo.nativePrivateClean,
+                        memInfo.hasSwappedOutPss ? memInfo.nativeSwappedOutPss :
+                        memInfo.nativeSwappedOut,
                         nativeMax, nativeAllocated, nativeFree);
                 printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
                         memInfo.dalvikPrivateDirty,
-                        memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut,
+                        memInfo.dalvikPrivateClean,
+                        memInfo.hasSwappedOutPss ? memInfo.dalvikSwappedOutPss :
+                        memInfo.dalvikSwappedOut,
                         dalvikMax, dalvikAllocated, dalvikFree);
             }
 
@@ -2183,6 +2180,7 @@
             int otherSharedClean = memInfo.otherSharedClean;
             int otherPrivateClean = memInfo.otherPrivateClean;
             int otherSwappedOut = memInfo.otherSwappedOut;
+            int otherSwappedOutPss = memInfo.otherSwappedOutPss;
 
             for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
                 final int myPss = memInfo.getOtherPss(i);
@@ -2192,16 +2190,22 @@
                 final int mySharedClean = memInfo.getOtherSharedClean(i);
                 final int myPrivateClean = memInfo.getOtherPrivateClean(i);
                 final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+                final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
                 if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
-                        || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) {
+                        || mySharedClean != 0 || myPrivateClean != 0
+                        || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
                     if (dumpFullInfo) {
                         printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                 myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
-                                mySharedClean, myPrivateClean, mySwappedOut, "", "", "");
+                                mySharedClean, myPrivateClean,
+                                memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                "", "", "");
                     } else {
                         printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                 myPss, myPrivateDirty,
-                                myPrivateClean, mySwappedOut, "", "", "");
+                                myPrivateClean,
+                                memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                "", "", "");
                     }
                     otherPss -= myPss;
                     otherSwappablePss -= mySwappablePss;
@@ -2210,26 +2214,32 @@
                     otherSharedClean -= mySharedClean;
                     otherPrivateClean -= myPrivateClean;
                     otherSwappedOut -= mySwappedOut;
+                    otherSwappedOutPss -= mySwappedOutPss;
                 }
             }
 
             if (dumpFullInfo) {
                 printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss,
                         otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
-                        otherSwappedOut, "", "", "");
+                        memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
+                        "", "", "");
                 printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(),
                         memInfo.getTotalSwappablePss(),
                         memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
-                        memInfo.getTotalSwappedOut(), nativeMax+dalvikMax,
-                        nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+                        memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOut() :
+                        memInfo.getTotalSwappedOutPss(),
+                        nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
+                        nativeFree+dalvikFree);
             } else {
                 printRow(pw, HEAP_COLUMN, "Unknown", otherPss,
-                        otherPrivateDirty, otherPrivateClean, otherSwappedOut,
+                        otherPrivateDirty, otherPrivateClean,
+                        memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
                         "", "", "");
                 printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
                         memInfo.getTotalPrivateDirty(),
                         memInfo.getTotalPrivateClean(),
+                        memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
                         memInfo.getTotalSwappedOut(),
                         nativeMax+dalvikMax,
                         nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
@@ -2248,16 +2258,22 @@
                     final int mySharedClean = memInfo.getOtherSharedClean(i);
                     final int myPrivateClean = memInfo.getOtherPrivateClean(i);
                     final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+                    final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
                     if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
-                            || mySharedClean != 0 || myPrivateClean != 0) {
+                            || mySharedClean != 0 || myPrivateClean != 0
+                            || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
                         if (dumpFullInfo) {
                             printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                     myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
-                                    mySharedClean, myPrivateClean, mySwappedOut, "", "", "");
+                                    mySharedClean, myPrivateClean,
+                                    memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                    "", "", "");
                         } else {
                             printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
                                     myPss, myPrivateDirty,
-                                    myPrivateClean, mySwappedOut, "", "", "");
+                                    myPrivateClean,
+                                    memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+                                    "", "", "");
                         }
                     }
                 }
@@ -2283,9 +2299,15 @@
         printRow(pw, ONE_COUNT_COLUMN,
             "System:", memInfo.getSummarySystem());
         pw.println(" ");
-        printRow(pw, TWO_COUNT_COLUMNS,
-            "TOTAL:", memInfo.getSummaryTotalPss(),
-            "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
+        if (memInfo.hasSwappedOutPss) {
+            printRow(pw, TWO_COUNT_COLUMNS,
+                "TOTAL:", memInfo.getSummaryTotalPss(),
+                "TOTAL SWAP PSS:", memInfo.getSummaryTotalSwapPss());
+        } else {
+            printRow(pw, TWO_COUNT_COLUMNS,
+                "TOTAL:", memInfo.getSummaryTotalPss(),
+                "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
+        }
     }
 
     public void registerOnActivityPausedListener(Activity activity,
@@ -4161,6 +4183,15 @@
                             r.pendingIntents = pendingNewIntents;
                         }
                     }
+
+                    // For each relaunch request, activity manager expects an answer
+                    if (!r.onlyLocalRequest && fromServer) {
+                        try {
+                            ActivityManagerNative.getDefault().activityRelaunched(token);
+                        } catch (RemoteException e) {
+                            e.printStackTrace();
+                        }
+                    }
                     break;
                 }
             }
@@ -4275,6 +4306,13 @@
         ActivityClientRecord r = mActivities.get(tmp.token);
         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
         if (r == null) {
+            if (!tmp.onlyLocalRequest) {
+                try {
+                    ActivityManagerNative.getDefault().activityRelaunched(tmp.token);
+                } catch (RemoteException e) {
+                    // If the system process has died, it's game over for everyone.
+                }
+            }
             return;
         }
 
@@ -4320,6 +4358,17 @@
         r.overrideConfig = tmp.overrideConfig;
 
         handleLaunchActivity(r, currentIntent);
+
+        if (!tmp.onlyLocalRequest) {
+            try {
+                ActivityManagerNative.getDefault().activityRelaunched(r.token);
+                if (r.window != null) {
+                    r.window.reportActivityRelaunched();
+                }
+            } catch (RemoteException e) {
+                // If the system process has died, it's game over for everyone.
+            }
+        }
     }
 
     private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
@@ -4673,6 +4722,87 @@
         }
     }
 
+    private static void setupJitProfileSupport(LoadedApk loadedApk, File cacheDir) {
+        final ApplicationInfo appInfo = loadedApk.getApplicationInfo();
+        if (isSharingRuntime(appInfo)) {
+            // If sharing is enabled we do not have a unique application
+            // in a process and therefore cannot rely on the package
+            // name inside the runtime.
+            return;
+        }
+        final List<String> codePaths = new ArrayList<>();
+        if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            codePaths.add(appInfo.sourceDir);
+        }
+        if (appInfo.splitSourceDirs != null) {
+            Collections.addAll(codePaths, appInfo.splitSourceDirs);
+        }
+
+        if (codePaths.isEmpty()) {
+            // If there are no code paths there's no need to setup a profile file and register with
+            // the runtime,
+            return;
+        }
+
+        // Add an extension to the file name to better reveal its intended use.
+        // Keep in sync with BackgroundDexOptService.
+        final String profileExtension = ".prof";
+        final File profileFile = new File(cacheDir, loadedApk.mPackageName + profileExtension);
+        if (!profileFile.exists()) {
+            FileDescriptor fd = null;
+            try {
+                final int permissions = 0600;  // read-write for user.
+                fd = Os.open(profileFile.getAbsolutePath(), OsConstants.O_CREAT, permissions);
+                Os.fchmod(fd, permissions);
+                Os.fchown(fd, appInfo.uid, appInfo.uid);
+            } catch (ErrnoException e) {
+                Log.w(TAG, "Unable to create jit profile file " + profileFile, e);
+                try {
+                    Os.unlink(profileFile.getAbsolutePath());
+                } catch (ErrnoException unlinkErr) {
+                    Log.v(TAG, "Unable to unlink jit profile file " + profileFile, unlinkErr);
+                }
+                return;
+            } finally {
+                IoUtils.closeQuietly(fd);
+            }
+        }
+
+        VMRuntime.registerAppInfo(profileFile.getAbsolutePath(), appInfo.dataDir,
+                codePaths.toArray(new String[codePaths.size()]));
+    }
+
+    /*
+     * Two possible indications that this package could be
+     * sharing its runtime with other packages:
+     *
+     * 1) the sharedUserId attribute is set in the manifest,
+     *    indicating a request to share a VM with other
+     *    packages with the same sharedUserId.
+     *
+     * 2) the application element of the manifest has an
+     *    attribute specifying a non-default process name,
+     *    indicating the desire to run in another packages VM.
+     */
+    private static boolean isSharingRuntime(ApplicationInfo appInfo) {
+        IPackageManager pm = getPackageManager();
+        android.content.pm.PackageInfo pi = null;
+        try {
+            pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
+        } catch (RemoteException e) {
+        }
+        if (pi != null) {
+            boolean sharedUserIdSet = (pi.sharedUserId != null);
+            boolean processNameNotDefault = (pi.applicationInfo != null) &&
+                    !appInfo.packageName.equals(pi.applicationInfo.processName);
+            boolean sharable = sharedUserIdSet || processNameNotDefault;
+            return sharable;
+        }
+        // We couldn't get information for the package. Be pessimistic and assume
+        // it's sharing the runtime.
+        return true;
+    }
+
     private void updateDefaultDensity() {
         if (mCurDefaultDisplayDpi != Configuration.DENSITY_DPI_UNDEFINED
                 && mCurDefaultDisplayDpi != DisplayMetrics.DENSITY_DEVICE
@@ -4876,12 +5006,14 @@
                         + "due to missing cache directory");
             }
 
-            // Use codeCacheDir to store generated/compiled graphics code
+            // Use codeCacheDir to store generated/compiled graphics code and jit profiling data.
             final File codeCacheDir = appContext.getCodeCacheDir();
             if (codeCacheDir != null) {
                 setupGraphicsSupport(data.info, codeCacheDir);
+                setupJitProfileSupport(data.info, codeCacheDir);
             } else {
-                Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
+                Log.e(TAG, "Unable to setupGraphicsSupport and setupJitProfileSupport " +
+                        "due to missing code-cache directory");
             }
         }
 
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index e28fb20..af840d0 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -894,7 +894,9 @@
             final View decor = getDecor();
             if (decor != null) {
                 final ViewRootImpl viewRoot = decor.getViewRootImpl();
-                viewRoot.setPausedForTransition(false);
+                if (viewRoot != null) {
+                    viewRoot.setPausedForTransition(false);
+                }
             }
             onTransitionsComplete();
         }
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 38562da..bf0bd79 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -206,7 +206,7 @@
     }
 
     private void startEnter() {
-        if (mEnterActivityOptions.isReturning()) {
+        if (mEnterTransitionCoordinator.isReturning()) {
             if (mExitingToView != null) {
                 mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
                         mExitingToView);
@@ -238,6 +238,7 @@
 
     public void onResume() {
         restoreExitedViews();
+        restoreReenteringViews();
     }
 
     public void clear() {
@@ -258,6 +259,15 @@
         }
     }
 
+    private void restoreReenteringViews() {
+        if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) {
+            mEnterTransitionCoordinator.forceViewsToAppear();
+            mExitingFrom = null;
+            mExitingTo = null;
+            mExitingToView = null;
+        }
+    }
+
     public boolean startExitBackTransition(final Activity activity) {
         if (mEnteringNames == null) {
             return false;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 77721e6..0afca9d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -42,7 +42,6 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
-import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageItemInfo;
@@ -214,10 +213,15 @@
     }
 
     @Override
-    public int[] getPackageGids(String packageName)
+    public int[] getPackageGids(String packageName) throws NameNotFoundException {
+        return getPackageGids(packageName, 0);
+    }
+
+    @Override
+    public int[] getPackageGids(String packageName, int flags)
             throws NameNotFoundException {
         try {
-            int[] gids = mPM.getPackageGids(packageName, mContext.getUserId());
+            int[] gids = mPM.getPackageGids(packageName, flags, mContext.getUserId());
             if (gids != null) {
                 return gids;
             }
@@ -229,10 +233,20 @@
     }
 
     @Override
-    public int getPackageUid(String packageName, int userHandle)
+    public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
+        return getPackageUidAsUser(packageName, flags, mContext.getUserId());
+    }
+
+    @Override
+    public int getPackageUidAsUser(String packageName, int userId) throws NameNotFoundException {
+        return getPackageUidAsUser(packageName, 0, userId);
+    }
+
+    @Override
+    public int getPackageUidAsUser(String packageName, int flags, int userId)
             throws NameNotFoundException {
         try {
-            int uid = mPM.getPackageUid(packageName, userHandle);
+            int uid = mPM.getPackageUid(packageName, flags, userId);
             if (uid >= 0) {
                 return uid;
             }
@@ -300,8 +314,14 @@
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
+        return getApplicationInfoAsUser(packageName, flags, mContext.getUserId());
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
         try {
-            ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, mContext.getUserId());
+            ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, userId);
             if (ai != null) {
                 // This is a temporary hack. Callers must use
                 // createPackageContext(packageName).getApplicationInfo() to
@@ -338,7 +358,6 @@
         }
     }
 
-
     @Override
     public ActivityInfo getActivityInfo(ComponentName className, int flags)
             throws NameNotFoundException {
@@ -591,12 +610,12 @@
     @SuppressWarnings("unchecked")
     @Override
     public List<PackageInfo> getInstalledPackages(int flags) {
-        return getInstalledPackages(flags, mContext.getUserId());
+        return getInstalledPackagesAsUser(flags, mContext.getUserId());
     }
 
     /** @hide */
     @Override
-    public List<PackageInfo> getInstalledPackages(int flags, int userId) {
+    public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
         try {
             ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
             return slice.getList();
@@ -780,7 +799,7 @@
      * @hide
      */
     @Override
-    public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags, int userId) {
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
         try {
             return mPM.queryIntentReceivers(
                 intent,
@@ -794,7 +813,7 @@
 
     @Override
     public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
-        return queryBroadcastReceivers(intent, flags, mContext.getUserId());
+        return queryBroadcastReceiversAsUser(intent, flags, mContext.getUserId());
     }
 
     @Override
@@ -1155,8 +1174,10 @@
         throw new NameNotFoundException("Package " + appPackageName + " doesn't exist");
     }
 
-    int mCachedSafeMode = -1;
-    @Override public boolean isSafeMode() {
+    volatile int mCachedSafeMode = -1;
+
+    @Override
+    public boolean isSafeMode() {
         try {
             if (mCachedSafeMode < 0) {
                 mCachedSafeMode = mPM.isSafeMode() ? 1 : 0;
@@ -1421,7 +1442,7 @@
     public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
                                String installerPackageName) {
         final VerificationParams verificationParams = new VerificationParams(null, null,
-                null, VerificationParams.NO_UID, null);
+                null, VerificationParams.NO_UID);
         installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
                 installerPackageName, verificationParams, null, mContext.getUserId());
     }
@@ -1429,9 +1450,9 @@
     @Override
     public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
             int flags, String installerPackageName, Uri verificationURI,
-            ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
+            ContainerEncryptionParams encryptionParams) {
         final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
-                null, VerificationParams.NO_UID, manifestDigest);
+                null, VerificationParams.NO_UID);
         installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
                 installerPackageName, verificationParams, encryptionParams, mContext.getUserId());
     }
@@ -1455,7 +1476,7 @@
     public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer, int flags,
                String installerPackageName, int userId) {
         final VerificationParams verificationParams = new VerificationParams(null, null,
-                null, VerificationParams.NO_UID, null);
+                null, VerificationParams.NO_UID);
         installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null,
                 userId);
     }
@@ -1463,10 +1484,10 @@
     @Override
     public void installPackageWithVerification(Uri packageURI,
             PackageInstallObserver observer, int flags, String installerPackageName,
-            Uri verificationURI, ManifestDigest manifestDigest,
+            Uri verificationURI,
             ContainerEncryptionParams encryptionParams) {
         final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
-                null, VerificationParams.NO_UID, manifestDigest);
+                null, VerificationParams.NO_UID);
         installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
                 encryptionParams, mContext.getUserId());
     }
@@ -1547,7 +1568,7 @@
     }
 
     @Override
-    public int getIntentVerificationStatus(String packageName, int userId) {
+    public int getIntentVerificationStatusAsUser(String packageName, int userId) {
         try {
             return mPM.getIntentVerificationStatus(packageName, userId);
         } catch (RemoteException e) {
@@ -1557,7 +1578,7 @@
     }
 
     @Override
-    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+    public boolean updateIntentVerificationStatusAsUser(String packageName, int status, int userId) {
         try {
             return mPM.updateIntentVerificationStatus(packageName, status, userId);
         } catch (RemoteException e) {
@@ -1587,7 +1608,7 @@
     }
 
     @Override
-    public String getDefaultBrowserPackageName(int userId) {
+    public String getDefaultBrowserPackageNameAsUser(int userId) {
         try {
             return mPM.getDefaultBrowserPackageName(userId);
         } catch (RemoteException e) {
@@ -1597,7 +1618,7 @@
     }
 
     @Override
-    public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
         try {
             return mPM.setDefaultBrowserPackageName(packageName, userId);
         } catch (RemoteException e) {
@@ -1868,7 +1889,7 @@
     }
 
     @Override
-    public void getPackageSizeInfo(String packageName, int userHandle,
+    public void getPackageSizeInfoAsUser(String packageName, int userHandle,
             IPackageStatsObserver observer) {
         try {
             mPM.getPackageSizeInfo(packageName, userHandle, observer);
@@ -1915,7 +1936,7 @@
     }
 
     @Override
-    public void addPreferredActivity(IntentFilter filter, int match,
+    public void addPreferredActivityAsUser(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity, int userId) {
         try {
             mPM.addPreferredActivity(filter, match, set, activity, userId);
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 984a186..a147cc8 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -719,7 +719,7 @@
                     Fragment f = op.fragment;
                     int containerId = f.mContainerId;
                     if (mManager.mAdded != null) {
-                        for (int i = 0; i < mManager.mAdded.size(); i++) {
+                        for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
                             Fragment old = mManager.mAdded.get(i);
                             if (FragmentManagerImpl.DEBUG) {
                                 Log.v(TAG,
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 569ab11..fab3740 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1762,10 +1762,6 @@
 
     @Override
     public Context createDeviceEncryptedStorageContext() {
-        if (!StorageManager.isFileBasedEncryptionEnabled()) {
-            return null;
-        }
-
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE)
                 | Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE;
         return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 8d7f347..bbf1607 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -23,7 +23,6 @@
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.os.Bundle;
-import android.text.format.DateUtils;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -49,19 +48,16 @@
     private static final String DAY = "day";
 
     private final DatePicker mDatePicker;
-    private final Calendar mCalendar;
 
     private OnDateSetListener mDateSetListener;
 
-    private boolean mTitleNeedsUpdate = true;
-
     /**
      * Creates a new date picker dialog for the current date using the parent
      * context's default date picker dialog theme.
      *
      * @param context the parent context
      */
-    public DatePickerDialog(Context context) {
+    public DatePickerDialog(@NonNull Context context) {
         this(context, 0);
     }
 
@@ -73,7 +69,7 @@
      *                   this dialog, or {@code 0} to use the parent
      *                   {@code context}'s default alert dialog theme
      */
-    public DatePickerDialog(Context context, @StyleRes int themeResId) {
+    public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) {
         super(context, resolveDialogTheme(context, themeResId));
 
         final Context themeContext = getContext();
@@ -85,11 +81,10 @@
         setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
         setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
 
-        mCalendar = Calendar.getInstance();
-
-        final int year = mCalendar.get(Calendar.YEAR);
-        final int monthOfYear = mCalendar.get(Calendar.MONTH);
-        final int dayOfMonth = mCalendar.get(Calendar.DAY_OF_MONTH);
+        final Calendar calendar = Calendar.getInstance();
+        final int year = calendar.get(Calendar.YEAR);
+        final int monthOfYear = calendar.get(Calendar.MONTH);
+        final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
         mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
         mDatePicker.init(year, monthOfYear, dayOfMonth, this);
         mDatePicker.setValidationCallback(mValidationCallback);
@@ -107,7 +102,7 @@
      * @param dayOfMonth the initially selected day of month (1-31, depending
      *                   on month)
      */
-    public DatePickerDialog(@Nullable Context context, @Nullable OnDateSetListener listener,
+    public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener,
             int year, int month, int dayOfMonth) {
         this(context, 0, listener, year, month, dayOfMonth);
     }
@@ -135,7 +130,7 @@
         mDatePicker.updateDate(year, month, dayOfMonth);
     }
 
-    static int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) {
+    static @StyleRes int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) {
         if (themeResId == 0) {
             final TypedValue outValue = new TypedValue();
             context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true);
@@ -146,9 +141,8 @@
     }
 
     @Override
-    public void onDateChanged(DatePicker view, int year, int month, int dayOfMonth) {
+    public void onDateChanged(@NonNull DatePicker view, int year, int month, int dayOfMonth) {
         mDatePicker.init(year, month, dayOfMonth, this);
-        updateTitle(year, month, dayOfMonth);
     }
 
     /**
@@ -161,7 +155,7 @@
     }
 
     @Override
-    public void onClick(DialogInterface dialog, int which) {
+    public void onClick(@NonNull DialogInterface dialog, int which) {
         switch (which) {
             case BUTTON_POSITIVE:
                 if (mDateSetListener != null) {
@@ -200,29 +194,6 @@
         mDatePicker.updateDate(year, month, dayOfMonth);
     }
 
-    private void updateTitle(int year, int month, int dayOfMonth) {
-        if (!mDatePicker.getCalendarViewShown()) {
-            mCalendar.set(Calendar.YEAR, year);
-            mCalendar.set(Calendar.MONTH, month);
-            mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
-
-            final String title = DateUtils.formatDateTime(mContext,
-                    mCalendar.getTimeInMillis(),
-                    DateUtils.FORMAT_SHOW_DATE
-                    | DateUtils.FORMAT_SHOW_WEEKDAY
-                    | DateUtils.FORMAT_SHOW_YEAR
-                    | DateUtils.FORMAT_ABBREV_MONTH
-                    | DateUtils.FORMAT_ABBREV_WEEKDAY);
-            setTitle(title);
-
-            mTitleNeedsUpdate = true;
-        } else if (mTitleNeedsUpdate) {
-            setTitle(R.string.date_picker_dialog_title);
-
-            mTitleNeedsUpdate = false;
-        }
-    }
-
     @Override
     public Bundle onSaveInstanceState() {
         final Bundle state = super.onSaveInstanceState();
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 6e8e2c4..79461b4 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -21,9 +21,8 @@
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
-import android.annotation.StringRes;
-
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.StyleRes;
 import android.content.ComponentName;
 import android.content.Context;
@@ -44,11 +43,11 @@
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
 import android.view.SearchEvent;
 import android.view.View;
 import android.view.View.OnCreateContextMenuListener;
@@ -60,8 +59,10 @@
 
 import com.android.internal.R;
 import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.PhoneWindow;
 
 import java.lang.ref.WeakReference;
+import java.util.List;
 
 /**
  * Base class for Dialogs.
@@ -1081,6 +1082,13 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+    }
+
+    /**
      * @return The activity associated with this dialog, or null if there is no associated activity.
      */
     private ComponentName getAssociatedActivity() {
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index fb0e79b..ed4bb28 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -27,6 +27,7 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkPolicyManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.provider.Downloads;
@@ -105,8 +106,17 @@
     public final static String COLUMN_LOCAL_URI = "local_uri";
 
     /**
-     * The pathname of the file where the download is stored.
+     * Path to the downloaded file on disk.
+     * <p>
+     * Note that apps may not have filesystem permissions to directly access
+     * this path. Instead of trying to open this path directly, apps should use
+     * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain access.
+     *
+     * @deprecated apps should transition to using
+     *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+     *             instead.
      */
+    @Deprecated
     public final static String COLUMN_LOCAL_FILENAME = "local_filename";
 
     /**
@@ -908,16 +918,22 @@
         }
     }
 
-    private ContentResolver mResolver;
-    private String mPackageName;
+    private final ContentResolver mResolver;
+    private final String mPackageName;
+
     private Uri mBaseUri = Downloads.Impl.CONTENT_URI;
+    private boolean mAccessFilename;
 
     /**
      * @hide
      */
-    public DownloadManager(ContentResolver resolver, String packageName) {
-        mResolver = resolver;
-        mPackageName = packageName;
+    public DownloadManager(Context context) {
+        mResolver = context.getContentResolver();
+        mPackageName = context.getPackageName();
+
+        // Callers can access filename columns when targeting old platform
+        // versions; otherwise we throw telling them it's deprecated.
+        mAccessFilename = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N;
     }
 
     /**
@@ -933,6 +949,11 @@
         }
     }
 
+    /** {@hide} */
+    public void setAccessFilename(boolean accessFilename) {
+        mAccessFilename = accessFilename;
+    }
+
     /**
      * Enqueue a new download.  The download will start automatically once the download manager is
      * ready to execute it and connectivity is available.
@@ -997,7 +1018,7 @@
         if (underlyingCursor == null) {
             return null;
         }
-        return new CursorTranslator(underlyingCursor, mBaseUri);
+        return new CursorTranslator(underlyingCursor, mBaseUri, mAccessFilename);
     }
 
     /**
@@ -1265,11 +1286,13 @@
      * underlying data.
      */
     private static class CursorTranslator extends CursorWrapper {
-        private Uri mBaseUri;
+        private final Uri mBaseUri;
+        private final boolean mAccessFilename;
 
-        public CursorTranslator(Cursor cursor, Uri baseUri) {
+        public CursorTranslator(Cursor cursor, Uri baseUri, boolean accessFilename) {
             super(cursor);
             mBaseUri = baseUri;
+            mAccessFilename = accessFilename;
         }
 
         @Override
@@ -1290,8 +1313,19 @@
 
         @Override
         public String getString(int columnIndex) {
-            return (getColumnName(columnIndex).equals(COLUMN_LOCAL_URI)) ? getLocalUri() :
-                    super.getString(columnIndex);
+            final String columnName = getColumnName(columnIndex);
+            switch (columnName) {
+                case COLUMN_LOCAL_URI:
+                    return getLocalUri();
+                case COLUMN_LOCAL_FILENAME:
+                    if (!mAccessFilename) {
+                        throw new IllegalArgumentException(
+                                "COLUMN_LOCAL_FILENAME is deprecated;"
+                                        + " use ContentResolver.openFileDescriptor() instead");
+                    }
+                default:
+                    return super.getString(columnIndex);
+            }
         }
 
         private String getLocalUri() {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index c6cc452..fe9cc80 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -30,6 +30,7 @@
 import android.view.ViewGroup;
 import android.view.ViewGroupOverlay;
 import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 import android.view.Window;
 import android.view.accessibility.AccessibilityEvent;
 
@@ -57,6 +58,7 @@
     private boolean mAreViewsReady;
     private boolean mIsViewsTransitionStarted;
     private Transition mEnterViewsTransition;
+    private OnPreDrawListener mViewsReadyListener;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -138,15 +140,16 @@
                 (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) {
             viewsReady(sharedElements);
         } else {
-            decor.getViewTreeObserver()
-                    .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-                        @Override
-                        public boolean onPreDraw() {
-                            decor.getViewTreeObserver().removeOnPreDrawListener(this);
-                            viewsReady(sharedElements);
-                            return true;
-                        }
-                    });
+            mViewsReadyListener = new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    mViewsReadyListener = null;
+                    decor.getViewTreeObserver().removeOnPreDrawListener(this);
+                    viewsReady(sharedElements);
+                    return true;
+                }
+            };
+            decor.getViewTreeObserver().addOnPreDrawListener(mViewsReadyListener);
         }
     }
 
@@ -241,6 +244,50 @@
         }
     }
 
+    /**
+     * This is called onResume. If an Activity is resuming and the transitions
+     * haven't started yet, force the views to appear. This is likely to be
+     * caused by the top Activity finishing before the transitions started.
+     * In that case, we can finish any transition that was started, but we
+     * should cancel any pending transition and just bring those Views visible.
+     */
+    public void forceViewsToAppear() {
+        if (!mIsReturning) {
+            return;
+        }
+        if (!mIsReadyForTransition) {
+            mIsReadyForTransition = true;
+            final ViewGroup decor = getDecor();
+            if (decor != null && mViewsReadyListener != null) {
+                decor.getViewTreeObserver().removeOnPreDrawListener(mViewsReadyListener);
+                mViewsReadyListener = null;
+            }
+            showViews(mTransitioningViews, true);
+            mSharedElements.clear();
+            mAllSharedElementNames.clear();
+            mTransitioningViews.clear();
+            mIsReadyForTransition = true;
+            viewsTransitionComplete();
+            sharedElementTransitionComplete();
+        } else {
+            if (!mSharedElementTransitionStarted) {
+                moveSharedElementsFromOverlay();
+                mSharedElementTransitionStarted = true;
+                showViews(mSharedElements, true);
+                mSharedElements.clear();
+                sharedElementTransitionComplete();
+            }
+            if (!mIsViewsTransitionStarted) {
+                mIsViewsTransitionStarted = true;
+                showViews(mTransitioningViews, true);
+                mTransitioningViews.clear();
+                viewsTransitionComplete();
+            }
+            cancelPendingTransitions();
+        }
+        mAreViewsReady = true;
+    }
+
     private void cancel() {
         if (!mIsCanceled) {
             mIsCanceled = true;
@@ -659,5 +706,4 @@
             }
         });
     }
-
 }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a73ad09..e163b1c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -409,9 +409,6 @@
     // If set this fragment is being removed from its activity.
     boolean mRemoving;
 
-    // True if the fragment is in the resumed state.
-    boolean mResumed;
-
     // Set to true if this fragment was instantiated from a layout file.
     boolean mFromLayout;
 
@@ -928,7 +925,7 @@
      * for the duration of {@link #onResume()} and {@link #onPause()} as well.
      */
     final public boolean isResumed() {
-        return mResumed;
+        return mState >= RESUMED;
     }
 
     /**
@@ -1630,7 +1627,6 @@
         mWho = null;
         mAdded = false;
         mRemoving = false;
-        mResumed = false;
         mFromLayout = false;
         mInLayout = false;
         mRestored = false;
@@ -2113,7 +2109,6 @@
         writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
         writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
         writer.print(" mRemoving="); writer.print(mRemoving);
-        writer.print(" mResumed="); writer.print(mResumed);
         writer.print(" mFromLayout="); writer.print(mFromLayout);
         writer.print(" mInLayout="); writer.println(mInLayout);
         writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
@@ -2208,6 +2203,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.noteStateNotSaved();
         }
+        mState = CREATED;
         mCalled = false;
         onCreate(savedInstanceState);
         if (!mCalled) {
@@ -2238,6 +2234,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.noteStateNotSaved();
         }
+        mState = ACTIVITY_CREATED;
         mCalled = false;
         onActivityCreated(savedInstanceState);
         if (!mCalled) {
@@ -2254,6 +2251,7 @@
             mChildFragmentManager.noteStateNotSaved();
             mChildFragmentManager.execPendingActions();
         }
+        mState = STARTED;
         mCalled = false;
         onStart();
         if (!mCalled) {
@@ -2273,6 +2271,7 @@
             mChildFragmentManager.noteStateNotSaved();
             mChildFragmentManager.execPendingActions();
         }
+        mState = RESUMED;
         mCalled = false;
         onResume();
         if (!mCalled) {
@@ -2389,6 +2388,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchPause();
         }
+        mState = STARTED;
         mCalled = false;
         onPause();
         if (!mCalled) {
@@ -2401,6 +2401,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchStop();
         }
+        mState = STOPPED;
         mCalled = false;
         onStop();
         if (!mCalled) {
@@ -2428,6 +2429,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchDestroyView();
         }
+        mState = CREATED;
         mCalled = false;
         onDestroyView();
         if (!mCalled) {
@@ -2443,6 +2445,7 @@
         if (mChildFragmentManager != null) {
             mChildFragmentManager.dispatchDestroy();
         }
+        mState = INITIALIZING;
         mCalled = false;
         onDestroy();
         if (!mCalled) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 696ccdb..84ae09d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1004,7 +1004,6 @@
                 case Fragment.STARTED:
                     if (newState > Fragment.STARTED) {
                         if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
-                        f.mResumed = true;
                         f.performResume();
                         // Get rid of this in case we saved it and never needed it.
                         f.mSavedFragmentState = null;
@@ -1017,7 +1016,6 @@
                     if (newState < Fragment.RESUMED) {
                         if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
                         f.performPause();
-                        f.mResumed = false;
                     }
                 case Fragment.STARTED:
                     if (newState < Fragment.STARTED) {
@@ -1096,6 +1094,8 @@
                             if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
                             if (!f.mRetaining) {
                                 f.performDestroy();
+                            } else {
+                                f.mState = Fragment.INITIALIZING;
                             }
 
                             f.mCalled = false;
@@ -1119,7 +1119,11 @@
             }
         }
         
-        f.mState = newState;
+        if (f.mState != newState) {
+            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
+                    + "expected state " + newState + " found " + f.mState);
+            f.mState = newState;
+        }
     }
     
     void moveToState(Fragment f) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 61b1bc8..5bb2cf5 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -92,7 +92,7 @@
             int userId) throws RemoteException;
     public boolean startNextMatchingActivity(IBinder callingActivity,
             Intent intent, Bundle options) throws RemoteException;
-    public int startActivityFromRecents(int taskId, int launchStackId, Bundle options)
+    public int startActivityFromRecents(int taskId, Bundle options)
             throws RemoteException;
     public boolean finishActivity(IBinder token, int code, Intent data, int finishTask)
             throws RemoteException;
@@ -122,6 +122,7 @@
             PersistableBundle persistentState, CharSequence description) throws RemoteException;
     public void activitySlept(IBinder token) throws RemoteException;
     public void activityDestroyed(IBinder token) throws RemoteException;
+    public void activityRelaunched(IBinder token) throws RemoteException;
     public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
             int[] verticalSizeConfigurations, int[] smallestWidthConfigurations)
             throws RemoteException;
@@ -145,7 +146,31 @@
     public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
             Rect initialBounds) throws RemoteException;
     public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
-    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
+    public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode)
+            throws RemoteException;
+
+    /**
+     * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
+     *
+     * @param dockedBounds The bounds for the docked stack.
+     * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which
+     *                             might be different from the stack bounds to allow more
+     *                             flexibility while resizing, or {@code null} if they should be the
+     *                             same as the stack bounds.
+     * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets.
+     *                                  When resizing, we usually "freeze" the layout of a task. To
+     *                                  achieve that, we also need to "freeze" the insets, which
+     *                                  gets achieved by changing task bounds but not bounds used
+     *                                  to calculate the insets in this transient state
+     * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or
+     *                            {@code null} if they should be the same as the stack bounds.
+     * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other
+     *                                 stacks.
+     * @throws RemoteException
+     */
+    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) throws RemoteException;
     public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
     public List<StackInfo> getAllStackInfos() throws RemoteException;
     public StackInfo getStackInfo(int stackId) throws RemoteException;
@@ -251,6 +276,15 @@
     public ParceledListSlice<UriPermission> getPersistedUriPermissions(
             String packageName, boolean incoming) throws RemoteException;
 
+    // Gets the URI permissions granted to an arbitrary package.
+    // NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
+    // granted to another packages (instead of those granted to it).
+    public ParceledListSlice<UriPermission> getGrantedUriPermissions(String packageName, int userId)
+            throws RemoteException;
+
+    // Clears the URI permissions granted to an arbitrary package.
+    public void clearGrantedUriPermissions(String packageName, int userId) throws RemoteException;
+
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting)
             throws RemoteException;
 
@@ -439,7 +473,7 @@
     public void registerUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException;
     public void unregisterUserSwitchObserver(IUserSwitchObserver observer) throws RemoteException;
 
-    public void requestBugReport(boolean progress) throws RemoteException;
+    public void requestBugReport(int bugreportType) throws RemoteException;
 
     public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason)
             throws RemoteException;
@@ -539,11 +573,11 @@
     public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException;
 
     public int getActivityStackId(IBinder token) throws RemoteException;
-    public void moveActivityToStack(IBinder token, int stackId) throws RemoteException;
+    public void exitFreeformMode(IBinder token) throws RemoteException;
 
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
 
-    public void moveTasksToFullscreenStack(int fromStackId) throws RemoteException;
+    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) throws RemoteException;
 
     public int getAppStartMode(int uid, String packageName) throws RemoteException;
 
@@ -555,6 +589,8 @@
 
     public void setVrMode(IBinder token, boolean enabled) throws RemoteException;
 
+    public boolean isAppForeground(int uid) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -908,7 +944,7 @@
     int STOP_BINDER_TRACKING_AND_DUMP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 341;
     int POSITION_TASK_IN_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 342;
     int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343;
-    int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
+    int EXIT_FREEFORM_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
     int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
     int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
@@ -920,6 +956,11 @@
     int IN_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 353;
     int KILL_PACKAGE_DEPENDENTS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 354;
     int ENTER_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 355;
-    int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 356;
+    int ACTIVITY_RELAUNCHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 356;
     int GET_URI_PERMISSION_OWNER_FOR_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 357;
+    int RESIZE_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 358;
+    int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 359;
+    int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 360;
+    int CLEAR_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 361;
+    int IS_APP_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 362;
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index abe9822..368b8ef 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -68,6 +68,9 @@
     void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
     void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
 
+    void requestBindListener(in ComponentName component);
+    void requestUnbindListener(in INotificationListener token);
+
     void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
 
     ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
@@ -78,6 +81,8 @@
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
     void setInterruptionFilter(String pkg, int interruptionFilter);
 
+    void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation);
+
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
     boolean isSystemConditionProviderEnabled(String path);
@@ -98,6 +103,7 @@
     boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
     boolean removeAutomaticZenRule(String id);
     boolean removeAutomaticZenRules(String packageName);
+    int getRuleInstanceCount(in ComponentName owner);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 4b0935c..560e22a 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -18,5 +18,9 @@
 
 /** @hide */
 oneway interface ITaskStackListener {
+    /** Called whenever there are changes to the state of tasks in a stack. */
     void onTaskStackChanged();
+
+    /** Called whenever an Activity is moved to the pinned stack from another stack. */
+    void onActivityPinned();
 }
diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java
index 09038d5..dacf4ea 100644
--- a/core/java/android/app/JobSchedulerImpl.java
+++ b/core/java/android/app/JobSchedulerImpl.java
@@ -46,6 +46,15 @@
     }
 
     @Override
+    public int scheduleAsPackage(JobInfo job, String packageName, int userId) {
+        try {
+            return mBinder.scheduleAsPackage(job, packageName, userId);
+        } catch (RemoteException e) {
+            return JobScheduler.RESULT_FAILURE;
+        }
+    }
+
+    @Override
     public void cancel(int jobId) {
         try {
             mBinder.cancel(jobId);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 85d9831..faf5b11 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -380,6 +380,18 @@
     }
 
     /**
+     * @hide
+     */
+    public int getRuleInstanceCount(ComponentName owner) {
+        INotificationManager service = getService();
+        try {
+            return service.getRuleInstanceCount(owner);
+        } catch (RemoteException e) {
+        }
+        return 0;
+    }
+
+    /**
      * Returns AutomaticZenRules owned by the caller.
      *
      * <p>
@@ -637,10 +649,12 @@
         public static final int SUPPRESSED_EFFECTS_UNSET = -1;
         public static final int SUPPRESSED_EFFECT_LIGHTS = 1 << 0;
         public static final int SUPPRESSED_EFFECT_PEEK = 1 << 1;
+        public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 2;
 
         private static final int[] ALL_SUPPRESSED_EFFECTS = {
                 SUPPRESSED_EFFECT_LIGHTS,
                 SUPPRESSED_EFFECT_PEEK,
+                SUPPRESSED_EFFECT_SCREEN_ON,
         };
 
         /**
@@ -750,6 +764,7 @@
             switch (effect) {
                 case SUPPRESSED_EFFECT_LIGHTS: return "SUPPRESSED_EFFECT_LIGHTS";
                 case SUPPRESSED_EFFECT_PEEK: return "SUPPRESSED_EFFECT_PEEK";
+                case SUPPRESSED_EFFECT_SCREEN_ON: return "SUPPRESSED_EFFECT_SCREEN_ON";
                 case SUPPRESSED_EFFECTS_UNSET: return "SUPPRESSED_EFFECTS_UNSET";
                 default: return "UNKNOWN_" + effect;
             }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 288a2cb..f3b1175 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -78,6 +78,8 @@
 import android.net.wifi.RttManager;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiScanner;
+import android.net.wifi.nan.IWifiNanManager;
+import android.net.wifi.nan.WifiNanManager;
 import android.net.wifi.p2p.IWifiP2pManager;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.net.wifi.passpoint.IWifiPasspointManager;
@@ -247,7 +249,7 @@
                 new CachedServiceFetcher<DownloadManager>() {
             @Override
             public DownloadManager createService(ContextImpl ctx) {
-                return new DownloadManager(ctx.getContentResolver(), ctx.getPackageName());
+                return new DownloadManager(ctx);
             }});
 
         registerService(Context.BATTERY_SERVICE, BatteryManager.class,
@@ -499,6 +501,18 @@
                 return new WifiP2pManager(service);
             }});
 
+        registerService(Context.WIFI_NAN_SERVICE, WifiNanManager.class,
+                new StaticServiceFetcher<WifiNanManager>() {
+            @Override
+            public WifiNanManager createService() {
+                IBinder b = ServiceManager.getService(Context.WIFI_NAN_SERVICE);
+                IWifiNanManager service = IWifiNanManager.Stub.asInterface(b);
+                if (service == null) {
+                    return null;
+                }
+                return new WifiNanManager(service);
+            }});
+
         registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class,
                 new CachedServiceFetcher<WifiScanner>() {
             @Override
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index f7848f9..dce2e51 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -22,6 +22,7 @@
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Point;
@@ -856,6 +857,7 @@
      *
      * @hide
      */
+    @TestApi
     public boolean grantRuntimePermission(String packageName, String permission,
             UserHandle userHandle) {
         synchronized (mLock) {
@@ -884,6 +886,7 @@
      *
      * @hide
      */
+    @TestApi
     public boolean revokeRuntimePermission(String packageName, String permission,
             UserHandle userHandle) {
         synchronized (mLock) {
@@ -1028,6 +1031,11 @@
                         float scale, float centerX, float centerY) {
                     /* do nothing */
                 }
+
+                @Override
+                public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
+                    /* do nothing */
+                }
             });
         }
     }
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 13e27e2..bd10267 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -28,6 +28,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.view.IWindowManager;
 import android.view.InputEvent;
 import android.view.SurfaceControl;
@@ -167,9 +168,10 @@
             throwIfShutdownLocked();
             throwIfNotConnectedLocked();
         }
+        int callingUserId = UserHandle.getCallingUserId();
         final long identity = Binder.clearCallingIdentity();
         try {
-            IBinder token = mAccessibilityManager.getWindowToken(windowId);
+            IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId);
             if (token == null) {
                 return false;
             }
@@ -186,9 +188,10 @@
             throwIfShutdownLocked();
             throwIfNotConnectedLocked();
         }
+        int callingUserId = UserHandle.getCallingUserId();
         final long identity = Binder.clearCallingIdentity();
         try {
-            IBinder token = mAccessibilityManager.getWindowToken(windowId);
+            IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId);
             if (token == null) {
                 return null;
             }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4416415..56b4249 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -16,12 +16,16 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This class provides access to the system uimode services.  These services
  * allow applications to control UI modes of the device.
@@ -92,18 +96,26 @@
      * when the user exits desk mode.
      */
     public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
-    
-    /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+
+    /** @hide */
+    @IntDef({MODE_NIGHT_AUTO, MODE_NIGHT_NO, MODE_NIGHT_YES})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NightMode {}
+
+    /**
+     * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
      * automatically switch night mode on and off based on the time.
      */
     public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
     
-    /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+    /**
+     * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
      * never run in night mode.
      */
     public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
     
-    /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+    /**
+     * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
      * always run in night mode.
      */
     public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
@@ -195,20 +207,28 @@
     }
 
     /**
-     * Sets the night mode.  Changes to the night mode are only effective when
-     * the car or desk mode is enabled on a device.
-     *
-     * <p>The mode can be one of:
+     * Sets the night mode.
+     * <p>
+     * The mode can be one of:
      * <ul>
-     *   <li><em>{@link #MODE_NIGHT_NO}<em> - sets the device into notnight
-     *       mode.</li>
-     *   <li><em>{@link #MODE_NIGHT_YES}</em> - sets the device into night mode.
-     *   </li>
-     *   <li><em>{@link #MODE_NIGHT_AUTO}</em> - automatic night/notnight switching
-     *       depending on the location and certain other sensors.</li>
+     *   <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+     *       {@code notnight} mode</li>
+     *   <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
+     *       {@code night} mode</li>
+     *   <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
+     *       {@code night} and {@code notnight} based on the device's current
+     *       location and certain other sensors</li>
      * </ul>
+     * <p>
+     * <strong>Note:</strong> On API 22 and below, changes to the night mode
+     * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
+     * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
+     * device. Starting in API 23, changes to night mode are always effective.
+     *
+     * @param mode the night mode to set
+     * @see #getNightMode()
      */
-    public void setNightMode(int mode) {
+    public void setNightMode(@NightMode int mode) {
         if (mService != null) {
             try {
                 mService.setNightMode(mode);
@@ -219,11 +239,20 @@
     }
 
     /**
-     * @return the currently configured night mode. May be one of
-     *         {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES},
-     *         {@link #MODE_NIGHT_AUTO}, or -1 on error.
+     * Returns the currently configured night mode.
+     * <p>
+     * May be one of:
+     * <ul>
+     *   <li>{@link #MODE_NIGHT_NO}</li>
+     *   <li>{@link #MODE_NIGHT_YES}</li>
+     *   <li>{@link #MODE_NIGHT_AUTO}</li>
+     *   <li>{@code -1} on error</li>
+     * </ul>
+     *
+     * @return the current night mode, or {@code -1} on error
+     * @see #setNightMode(int)
      */
-    public int getNightMode() {
+    public @NightMode int getNightMode() {
         if (mService != null) {
             try {
                 return mService.getNightMode();
@@ -250,9 +279,13 @@
     }
 
     /**
-     * @return If Night mode is locked or not. When Night mode is locked, changing Night mode
-     *         is only allowed to privileged system components and normal application's call
-     *         to change Night mode using {@link #setNightMode(int)} will silently fail.
+     * Returns whether night mode is locked or not.
+     * <p>
+     * When night mode is locked, only privileged system components may change
+     * night mode and calls from non-privileged applications to change night
+     * mode will fail silently.
+     *
+     * @return {@code true} if night mode is locked or {@code false} otherwise
      */
     public boolean isNightModeLocked() {
         if (mService != null) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index eda0982..45b23d0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -88,13 +88,15 @@
 
     private final Context mContext;
     private final IDevicePolicyManager mService;
+    private boolean mParentInstance;
 
     private static final String REMOTE_EXCEPTION_MESSAGE =
             "Failed to talk with device policy manager service";
 
-    private DevicePolicyManager(Context context) {
+    private DevicePolicyManager(Context context, boolean parentInstance) {
         this(context, IDevicePolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)));
+        mParentInstance = parentInstance;
     }
 
     /** @hide */
@@ -106,7 +108,7 @@
 
     /** @hide */
     public static DevicePolicyManager create(Context context) {
-        DevicePolicyManager me = new DevicePolicyManager(context);
+        DevicePolicyManager me = new DevicePolicyManager(context, false);
         return me.mService != null ? me : null;
     }
 
@@ -618,6 +620,21 @@
             "android.app.extra.PROVISIONING_LOGO_URI";
 
     /**
+     * A boolean extra indicating if user setup should be skipped, for when provisioning is started
+     * during setup-wizard.
+     *
+     * <p>If unspecified, defaults to {@code true} to match the behavior in
+     * {@link android.os.Build.VERSION_CODES#M} and earlier.
+     *
+     * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE},
+     * {@link #ACTION_PROVISION_MANAGED_DEVICE} or {@link #ACTION_PROVISION_MANAGED_USER}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PROVISIONING_SKIP_USER_SETUP =
+            "android.app.extra.PROVISIONING_SKIP_USER_SETUP";
+
+    /**
      * This MIME type is used for starting the Device Owner provisioning.
      *
      * <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -756,12 +773,28 @@
      * have the user select a new password in order to meet the current
      * constraints. Upon being resumed from this activity, you can check the new
      * password characteristics to see if they are sufficient.
+     *
+     * If the intent is launched from within a managed profile with a profile
+     * owner built against {@link android.os.Build.VERSION_CODES#M} or before,
+     * this will trigger entering a new password for the parent of the profile.
+     * For all other cases it will trigger entering a new password for the user
+     * or profile it is launched from.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SET_NEW_PASSWORD
             = "android.app.action.SET_NEW_PASSWORD";
 
     /**
+     * Activity action: have the user enter a new password for the parent profile.
+     * If the intent is launched from within a managed profile, this will trigger
+     * entering a new password for the parent of the profile. In all other cases
+     * the behaviour is identical to {@link #ACTION_SET_NEW_PASSWORD}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD
+            = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
+
+    /**
      * Flag used by {@link #addCrossProfileIntentFilter} to allow activities in
      * the parent profile to access intents sent from the managed profile.
      * That is, when an app in the managed profile calls
@@ -939,6 +972,22 @@
     }
 
     /**
+     * Returns true if the Profile Challenge is available to use for the given profile user.
+     *
+     * @hide
+     */
+    public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.isSeparateProfileChallengeAllowed(userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Constant for {@link #setPasswordQuality}: the policy has no requirements
      * for the password.  Note that quality constants are ordered so that higher
      * values are more restrictive.
@@ -1031,7 +1080,7 @@
     public void setPasswordQuality(@NonNull ComponentName admin, int quality) {
         if (mService != null) {
             try {
-                mService.setPasswordQuality(admin, quality);
+                mService.setPasswordQuality(admin, quality, mParentInstance);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -1052,7 +1101,7 @@
     public int getPasswordQuality(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
-                return mService.getPasswordQuality(admin, userHandle);
+                return mService.getPasswordQuality(admin, userHandle, mParentInstance);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -1622,7 +1671,7 @@
     public boolean isActivePasswordSufficient() {
         if (mService != null) {
             try {
-                return mService.isActivePasswordSufficient(myUserId());
+                return mService.isActivePasswordSufficient(myUserId(), mParentInstance);
             } catch (RemoteException e) {
                 Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
@@ -1791,6 +1840,9 @@
      * not acceptable for the current constraints or if the user has not been decrypted yet.
      */
     public boolean resetPassword(String password, int flags) {
+        if (mParentInstance) {
+            throw new SecurityException("Reset password does not work across profiles.");
+        }
         if (mService != null) {
             try {
                 return mService.resetPassword(password, flags);
@@ -2415,6 +2467,53 @@
     }
 
     /**
+     * Called by a device or profile owner to configure an always-on VPN connection through a
+     * specific application for the current user.
+     * This connection is automatically granted and persisted after a reboot.
+     *
+     * <p>The designated package should declare a {@link android.net.VpnService} in its
+     *    manifest guarded by {@link android.Manifest.permission#BIND_VPN_SERVICE},
+     *    otherwise the call will fail.
+     *
+     * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+     *                   to remove an existing always-on VPN configuration.
+     *
+     * @return {@code true} if the package is set as always-on VPN controller;
+     *         {@code false} otherwise.
+     */
+    public boolean setAlwaysOnVpnPackage(@NonNull ComponentName admin,
+            @Nullable String vpnPackage) {
+        if (mService != null) {
+            try {
+                return mService.setAlwaysOnVpnPackage(admin, vpnPackage);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called by a device or profile owner to read the name of the package administering an
+     * always-on VPN connection for the current user.
+     * If there is no such package, or the always-on VPN is provided by the system instead
+     * of by an application, {@code null} will be returned.
+     *
+     * @return Package name of VPN controller responsible for always-on VPN,
+     *         or {@code null} if none is set.
+     */
+    public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getAlwaysOnVpnPackage(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Called by an application that is administering the device to disable all cameras
      * on the device, for this user. After setting this, no applications running as this user
      * will be able to access any cameras on the device.
@@ -3060,6 +3159,8 @@
      *
      * <p>If the device owner information is {@code null} or empty then the device owner info is
      * cleared and the user owner info is shown on the lock screen if it is set.
+     * <p>If the device owner information contains only whitespaces then the message on the lock
+     * screen will be blank and the user will not be allowed to change it.
      *
      * @param admin The name of the admin component to check.
      * @param info Device owner information which will be displayed instead of the user
@@ -3533,6 +3634,66 @@
     }
 
     /**
+     * Called by a profile owner of a managed profile to set whether contacts search from
+     * the managed profile will be shown in the parent profile, for incoming calls.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param disabled If true contacts search in the managed profile is not displayed.
+     */
+    public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
+            boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setCrossProfileContactsSearchDisabled(admin, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner of a managed profile to determine whether or not contacts search
+     * has been disabled.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     */
+    public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getCrossProfileContactsSearchDisabled(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Determine whether or not contacts search has been disabled.
+     *
+     * @param userHandle The user for whom to check the contacts search permission
+     * @hide
+     */
+    public boolean getCrossProfileContactsSearchDisabled(@NonNull UserHandle userHandle) {
+        if (mService != null) {
+            try {
+                return mService
+                        .getCrossProfileContactsSearchDisabledForUser(userHandle.getIdentifier());
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Start Quick Contact on the managed profile for the user, if the policy allows.
      * @hide
      */
@@ -4802,4 +4963,148 @@
             Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
         }
     }
+
+    /**
+     * Called by a device admin to set the short support message. This will
+     * be displayed to the user in settings screens where funtionality has
+     * been disabled by the admin.
+     *
+     * The message should be limited to a short statement such as
+     * "This setting is disabled by your administrator. Contact someone@example.com
+     *  for support."
+     * If the message is longer than 200 characters it may be truncated.
+     *
+     * @see #setLongSupportMessage
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param message Short message to be displayed to the user in settings or null to
+     *        clear the existing message.
+     */
+    public void setShortSupportMessage(@NonNull ComponentName admin,
+            @Nullable String message) {
+        if (mService != null) {
+            try {
+                mService.setShortSupportMessage(admin, message);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Called by a device admin to get the short support message.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return The message set by {@link #setShortSupportMessage(ComponentName, String)}
+     *         or null if no message has been set.
+     */
+    public String getShortSupportMessage(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getShortSupportMessage(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called by a device admin to set the long support message. This will
+     * be displayed to the user in the device administators settings screen.
+     *
+     * @see #setShortSupportMessage
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param message Long message to be displayed to the user in settings or null to
+     *        clear the existing message.
+     */
+    public void setLongSupportMessage(@NonNull ComponentName admin,
+            @Nullable String message) {
+        if (mService != null) {
+            try {
+                mService.setLongSupportMessage(admin, message);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+    }
+
+    /**
+     * Called by a device admin to get the long support message.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return The message set by {@link #setLongSupportMessage(ComponentName, String)}
+     *         or null if no message has been set.
+     */
+    public String getLongSupportMessage(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getLongSupportMessage(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called by the system to get the short support message.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param userHandle user id the admin is running as.
+     * @return The message set by {@link #setShortSupportMessage(ComponentName, String)}
+     *
+     * @hide
+     */
+    public String getShortSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.getShortSupportMessageForUser(admin, userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Called by the system to get the long support message.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param userHandle user id the admin is running as.
+     * @return The message set by {@link #setLongSupportMessage(ComponentName, String)}
+     *
+     * @hide
+     */
+    public String getLongSupportMessageForUser(@NonNull ComponentName admin, int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.getLongSupportMessageForUser(admin, userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Obtains a {@link DevicePolicyManager} whose calls act on the parent profile.
+     *
+     * <p> Note only some methods will work on the parent Manager.
+     *
+     * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
+     */
+    public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+        try {
+            if (!mService.isManagedProfile(admin)) {
+                throw new SecurityException("The current user does not have a parent profile.");
+            }
+            return new DevicePolicyManager(mContext, true);
+        } catch (RemoteException re) {
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 30ce682..7771440 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -35,8 +35,8 @@
  * {@hide}
  */
 interface IDevicePolicyManager {
-    void setPasswordQuality(in ComponentName who, int quality);
-    int getPasswordQuality(in ComponentName who, int userHandle);
+    void setPasswordQuality(in ComponentName who, int quality, boolean parent);
+    int getPasswordQuality(in ComponentName who, int userHandle, boolean parent);
 
     void setPasswordMinimumLength(in ComponentName who, int length);
     int getPasswordMinimumLength(in ComponentName who, int userHandle);
@@ -67,7 +67,7 @@
 
     long getPasswordExpiration(in ComponentName who, int userHandle);
 
-    boolean isActivePasswordSufficient(int userHandle);
+    boolean isActivePasswordSufficient(int userHandle, boolean parent);
     int getCurrentFailedPasswordAttempts(int userHandle);
     int getProfileWithMinimumFailedPasswordsForWipe(int userHandle);
 
@@ -144,6 +144,9 @@
     void setCertInstallerPackage(in ComponentName who, String installerPackage);
     String getCertInstallerPackage(in ComponentName who);
 
+    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage);
+    String getAlwaysOnVpnPackage(in ComponentName who);
+
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
     void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
 
@@ -202,6 +205,9 @@
     void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
     boolean getCrossProfileCallerIdDisabled(in ComponentName who);
     boolean getCrossProfileCallerIdDisabledForUser(int userId);
+    void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled);
+    boolean getCrossProfileContactsSearchDisabled(in ComponentName who);
+    boolean getCrossProfileContactsSearchDisabledForUser(int userId);
     void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent);
 
     void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
@@ -245,4 +251,14 @@
     boolean isSystemOnlyUser(in ComponentName admin);
     String getWifiMacAddress();
     void reboot(in ComponentName admin);
+
+    void setShortSupportMessage(in ComponentName admin, in String message);
+    String getShortSupportMessage(in ComponentName admin);
+    void setLongSupportMessage(in ComponentName admin, in String message);
+    String getLongSupportMessage(in ComponentName admin);
+
+    String getShortSupportMessageForUser(in ComponentName admin, int userHandle);
+    String getLongSupportMessageForUser(in ComponentName admin, int userHandle);
+
+    boolean isSeparateProfileChallengeAllowed(int userHandle);
 }
diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl
index f1258ae..f0c3302 100644
--- a/core/java/android/app/job/IJobScheduler.aidl
+++ b/core/java/android/app/job/IJobScheduler.aidl
@@ -24,6 +24,7 @@
   */
 interface IJobScheduler {
     int schedule(in JobInfo job);
+    int scheduleAsPackage(in JobInfo job, String packageName, int userId);
     void cancel(int jobId);
     void cancelAll();
     List<JobInfo> getAllPendingJobs();
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 0d9e778..9ad35d4 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -64,6 +64,11 @@
      */
     public static final int BACKOFF_POLICY_EXPONENTIAL = 1;
 
+    /* Minimum interval for a periodic job, in milliseconds. */
+    public static final long MIN_PERIOD_MILLIS = 60 * 60 * 1000L;   // 60 minutes
+    /* Minimum flex for a periodic job, in milliseconds. */
+    public static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
+
     /**
      * Default type of backoff.
      * @hide
@@ -83,8 +88,10 @@
     private final boolean isPeriodic;
     private final boolean isPersisted;
     private final long intervalMillis;
+    private final long flexMillis;
     private final long initialBackoffMillis;
     private final int backoffPolicy;
+    private final int priority;
 
     /**
      * Unique job id associated with this class. This is assigned to your job by the scheduler.
@@ -107,6 +114,11 @@
         return service;
     }
 
+    /** @hide */
+    public int getPriority() {
+        return priority;
+    }
+
     /**
      * Whether this job needs the device to be plugged in.
      */
@@ -165,7 +177,17 @@
      * job does not recur periodically.
      */
     public long getIntervalMillis() {
-        return intervalMillis;
+        return intervalMillis >= MIN_PERIOD_MILLIS ? intervalMillis : MIN_PERIOD_MILLIS;
+    }
+
+    /**
+     * Flex time for this job. Only valid if this is a periodic job.
+     */
+    public long getFlexMillis() {
+        long interval = getIntervalMillis();
+        long percentClamp = 5 * interval / 100;
+        long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, MIN_FLEX_MILLIS));
+        return clampedFlex <= interval ? clampedFlex : interval;
     }
 
     /**
@@ -216,10 +238,12 @@
         isPeriodic = in.readInt() == 1;
         isPersisted = in.readInt() == 1;
         intervalMillis = in.readLong();
+        flexMillis = in.readLong();
         initialBackoffMillis = in.readLong();
         backoffPolicy = in.readInt();
         hasEarlyConstraint = in.readInt() == 1;
         hasLateConstraint = in.readInt() == 1;
+        priority = in.readInt();
     }
 
     private JobInfo(JobInfo.Builder b) {
@@ -234,10 +258,12 @@
         isPeriodic = b.mIsPeriodic;
         isPersisted = b.mIsPersisted;
         intervalMillis = b.mIntervalMillis;
+        flexMillis = b.mFlexMillis;
         initialBackoffMillis = b.mInitialBackoffMillis;
         backoffPolicy = b.mBackoffPolicy;
         hasEarlyConstraint = b.mHasEarlyConstraint;
         hasLateConstraint = b.mHasLateConstraint;
+        priority = b.mPriority;
     }
 
     @Override
@@ -258,10 +284,12 @@
         out.writeInt(isPeriodic ? 1 : 0);
         out.writeInt(isPersisted ? 1 : 0);
         out.writeLong(intervalMillis);
+        out.writeLong(flexMillis);
         out.writeLong(initialBackoffMillis);
         out.writeInt(backoffPolicy);
         out.writeInt(hasEarlyConstraint ? 1 : 0);
         out.writeInt(hasLateConstraint ? 1 : 0);
+        out.writeInt(priority);
     }
 
     public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
@@ -286,6 +314,7 @@
         private int mJobId;
         private PersistableBundle mExtras = PersistableBundle.EMPTY;
         private ComponentName mJobService;
+        private int mPriority;
         // Requirements.
         private boolean mRequiresCharging;
         private boolean mRequiresDeviceIdle;
@@ -299,6 +328,7 @@
         private boolean mHasEarlyConstraint;
         private boolean mHasLateConstraint;
         private long mIntervalMillis;
+        private long mFlexMillis;
         // Back-off parameters.
         private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
         private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
@@ -318,6 +348,14 @@
         }
 
         /**
+         * @hide
+         */
+        public Builder setPriority(int priority) {
+            mPriority = priority;
+            return this;
+        }
+
+        /**
          * Set optional extras. This is persisted, so we only allow primitive types.
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
          */
@@ -373,8 +411,21 @@
          * @param intervalMillis Millisecond interval for which this job will repeat.
          */
         public Builder setPeriodic(long intervalMillis) {
+            return setPeriodic(intervalMillis, intervalMillis);
+        }
+
+        /**
+         * Specify that this job should recur with the provided interval and flex. The job can
+         * execute at any time in a window of flex length at the end of the period.
+         * @param intervalMillis Millisecond interval for which this job will repeat.
+         * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
+         *                   {@link #MIN_FLEX_MILLIS} or 5 percent of the period, whichever is
+         *                   higher.
+         */
+        public Builder setPeriodic(long intervalMillis, long flexMillis) {
             mIsPeriodic = true;
             mIntervalMillis = intervalMillis;
+            mFlexMillis = flexMillis;
             mHasEarlyConstraint = mHasLateConstraint = true;
             return this;
         }
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 7ee39f5..a0a60e8 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -28,10 +28,22 @@
  */
 public class JobParameters implements Parcelable {
 
+    /** @hide */
+    public static final int REASON_CANCELED = 0;
+    /** @hide */
+    public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+    /** @hide */
+    public static final int REASON_PREEMPT = 2;
+    /** @hide */
+    public static final int REASON_TIMEOUT = 3;
+    /** @hide */
+    public static final int REASON_DEVICE_IDLE = 4;
+
     private final int jobId;
     private final PersistableBundle extras;
     private final IBinder callback;
     private final boolean overrideDeadlineExpired;
+    private int stopReason; // Default value of stopReason is REASON_CANCELED
 
     /** @hide */
     public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
@@ -50,6 +62,14 @@
     }
 
     /**
+     * Reason onStopJob() was called on this job.
+     * @hide
+     */
+    public int getStopReason() {
+        return stopReason;
+    }
+
+    /**
      * @return The extras you passed in when constructing this job with
      * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
      * never be null. If you did not set any extras this will be an empty bundle.
@@ -78,6 +98,12 @@
         extras = in.readPersistableBundle();
         callback = in.readStrongBinder();
         overrideDeadlineExpired = in.readInt() == 1;
+        stopReason = in.readInt();
+    }
+
+    /** @hide */
+    public void setStopReason(int reason) {
+        stopReason = reason;
     }
 
     @Override
@@ -91,6 +117,7 @@
         dest.writePersistableBundle(extras);
         dest.writeStrongBinder(callback);
         dest.writeInt(overrideDeadlineExpired ? 1 : 0);
+        dest.writeInt(stopReason);
     }
 
     public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
index a1c11f3..5e1a4256 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -50,22 +50,30 @@
      */
     public static final int RESULT_FAILURE = 0;
     /**
-     * Returned from {@link #schedule(JobInfo)} if this application has made too many requests for
-     * work over too short a time.
+     * Returned from {@link #schedule(JobInfo)} if this job has been successfully scheduled.
      */
-    // TODO: Determine if this is necessary.
     public static final int RESULT_SUCCESS = 1;
 
     /**
      * @param job The job you wish scheduled. See
      * {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
      * you can schedule.
-     * @return If >0, this int returns the jobId of the successfully scheduled job.
-     * Otherwise you have to compare the return value to the error codes defined in this class.
+     * @return An int representing ({@link #RESULT_SUCCESS} or {@link #RESULT_FAILURE}).
      */
     public abstract int schedule(JobInfo job);
 
     /**
+     *
+     * @param job The job to be scheduled.
+     * @param packageName The package on behalf of which the job is to be scheduled. This will be
+     *                    used to track battery usage and appIdleState.
+     * @param userId    User on behalf of whom this job is to be scheduled.
+     * @return {@link #RESULT_SUCCESS} or {@link #RESULT_FAILURE}
+     * @hide
+     */
+    public abstract int scheduleAsPackage(JobInfo job, String packageName, int userId);
+
+    /**
      * Cancel a job that is pending in the JobScheduler.
      * @param jobId unique identifier for this job. Obtain this value from the jobs returned by
      * {@link #getAllPendingJobs()}.
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index ee591d3..88ba874 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,7 +16,9 @@
 
 package android.app.trust;
 
+import android.Manifest;
 import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -54,9 +56,12 @@
      * Changes the lock status for the given user. This is only applicable to Managed Profiles,
      * other users should be handled by Keyguard.
      *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     *
      * @param userId The id for the user to be locked/unlocked.
      * @param locked The value for that user's locked state.
      */
+    @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
     public void setDeviceLockedForUser(int userId, boolean locked) {
         try {
             mService.setDeviceLockedForUser(userId, locked);
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index ef08eb9..5f97c9e 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -121,12 +121,12 @@
      */
     public static class Bucket {
         /**
-         * Combined usage across all other states.
+         * Combined usage across all states.
          */
         public static final int STATE_ALL = -1;
 
         /**
-         * Usage not accounted in any other states.
+         * Usage not accounted for in any other state.
          */
         public static final int STATE_DEFAULT = 0x1;
 
@@ -150,8 +150,40 @@
          */
         public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
 
+        /**
+         * Combined usage across all metering states.
+         */
+        public static final int METERING_ALL = -1;
+
+        /**
+         * Usage not accounted for in any other metering state.
+         */
+        public static final int METERING_DEFAULT = 0x1;
+
+        /**
+         * Metered usage.
+         */
+        public static final int METERING_METERED = 0x2;
+
+        /**
+         * Combined usage across all roaming states.
+         */
+        public static final int ROAMING_ALL = -1;
+
+        /**
+         * Usage not accounted for in any other roaming state.
+         */
+        public static final int ROAMING_DEFAULT = 0x1;
+
+        /**
+         * Roaming usage.
+         */
+        public static final int ROAMING_ROAMING = 0x2;
+
         private int mUid;
         private int mState;
+        private int mMetering;
+        private int mRoaming;
         private long mBeginTimeStamp;
         private long mEndTimeStamp;
         private long mRxBytes;
@@ -206,6 +238,30 @@
         }
 
         /**
+         * Metering state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #METERING_ALL}</li>
+         * <li>{@link #METERING_DEFAULT}</li>
+         * <li>{@link #METERING_METERED}</li>
+         * </ul>
+         */
+        public int getMetering() {
+            return mMetering;
+        }
+
+        /**
+         * Roaming state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #ROAMING_ALL}</li>
+         * <li>{@link #ROAMING_DEFAULT}</li>
+         * <li>{@link #ROAMING_ROAMING}</li>
+         * </ul>
+         */
+        public int getRoaming() {
+            return mRoaming;
+        }
+
+        /**
          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
          * {@link java.lang.System#currentTimeMillis}.
          * @return Start of interval.
@@ -398,6 +454,9 @@
     private void fillBucketFromSummaryEntry(Bucket bucketOut) {
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+        // TODO: Implement metering/roaming tracking.
+        bucketOut.mMetering = Bucket.METERING_ALL;
+        bucketOut.mRoaming = Bucket.ROAMING_ALL;
         bucketOut.mBeginTimeStamp = mStartTimeStamp;
         bucketOut.mEndTimeStamp = mEndTimeStamp;
         bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
@@ -444,6 +503,8 @@
                         mRecycledHistoryEntry);
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mMetering = Bucket.METERING_ALL;
+                bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
                 bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
                         mRecycledHistoryEntry.bucketDuration;
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
old mode 100644
new mode 100755
index 2e27345..74302f2
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -371,6 +371,89 @@
     }
 
     /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     *  Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+     * {@link #PRIORITY_OFF},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON){
+                return false;
+            }
+            try {
+                return mService.setPriority(device, priority);
+            } catch (RemoteException e) {
+                   Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                   return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+    }
+
+    /**
+     * Get the priority of the profile.
+     *
+     * <p> The priority can be any of:
+     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     * @hide
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) log("getPriority(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getPriority(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.PRIORITY_OFF;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.PRIORITY_OFF;
+    }
+
+    /**
+     * Check if A2DP profile is streaming music.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param device BluetoothDevice device
+     */
+    public boolean isA2dpPlaying(BluetoothDevice device) {
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.isA2dpPlaying(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
      * Helper for converting a state to a string.
      *
      * For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index b53a8fc..444e429 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -20,6 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -28,8 +30,8 @@
 import java.util.List;
 
 /**
- * This class provides the public APIs to control the Bluetooth AVRCP Controller
- * profile.
+ * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
+ * supports player information, playback support and track metadata.
  *
  *<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
  * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
@@ -39,7 +41,7 @@
  */
 public final class BluetoothAvrcpController implements BluetoothProfile {
     private static final String TAG = "BluetoothAvrcpController";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final boolean VDBG = false;
 
     /**
@@ -61,7 +63,63 @@
      * receive.
      */
     public static final String ACTION_CONNECTION_STATE_CHANGED =
-        "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
+        "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in metadata state of playing track on the AVRCP
+     * AG.
+     *
+     * <p>This intent will have the two extras:
+     * <ul>
+     *    <li> {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.</li>
+     *    <li> {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback
+     *    state. </li>
+     * </ul>
+     */
+    public static final String ACTION_TRACK_EVENT =
+        "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT";
+
+
+    /**
+     * Intent used to broadcast the change in player application setting state on AVRCP AG.
+     *
+     * <p>This intent will have the following extras:
+     * <ul>
+     *    <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
+     *    most recent player setting. </li>
+     * </ul>
+     */
+    public static final String ACTION_PLAYER_SETTING =
+        "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
+
+    public static final String EXTRA_METADATA =
+            "android.bluetooth.avrcp-controller.profile.extra.METADATA";
+
+    public static final String EXTRA_PLAYBACK =
+            "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";
+
+    public static final String EXTRA_PLAYER_SETTING =
+            "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
+
+    /*
+     * KeyCoded for Pass Through Commands
+     */
+    public static final int PASS_THRU_CMD_ID_PLAY = 0x44;
+    public static final int PASS_THRU_CMD_ID_PAUSE = 0x46;
+    public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41;
+    public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42;
+    public static final int PASS_THRU_CMD_ID_STOP = 0x45;
+    public static final int PASS_THRU_CMD_ID_FF = 0x49;
+    public static final int PASS_THRU_CMD_ID_REWIND = 0x48;
+    public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B;
+    public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C;
+    /* Key State Variables */
+    public static final int KEY_STATE_PRESSED = 0;
+    public static final int KEY_STATE_RELEASED = 1;
+    /* Group Navigation Key Codes */
+    public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00;
+    public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01;
+
 
     private Context mContext;
     private ServiceListener mServiceListener;
@@ -69,33 +127,33 @@
     private BluetoothAdapter mAdapter;
 
     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-            new IBluetoothStateChangeCallback.Stub() {
-                public void onBluetoothStateChange(boolean up) {
-                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                    if (!up) {
-                        if (VDBG) Log.d(TAG,"Unbinding service...");
-                        synchronized (mConnection) {
-                            try {
-                                mService = null;
-                                mContext.unbindService(mConnection);
-                            } catch (Exception re) {
-                                Log.e(TAG,"",re);
-                            }
+        new IBluetoothStateChangeCallback.Stub() {
+            public void onBluetoothStateChange(boolean up) {
+                if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+                if (!up) {
+                    if (VDBG) Log.d(TAG,"Unbinding service...");
+                    synchronized (mConnection) {
+                        try {
+                            mService = null;
+                            mContext.unbindService(mConnection);
+                        } catch (Exception re) {
+                            Log.e(TAG,"",re);
                         }
-                    } else {
-                        synchronized (mConnection) {
-                            try {
-                                if (mService == null) {
-                                    if (VDBG) Log.d(TAG,"Binding service...");
-                                    doBind();
-                                }
-                            } catch (Exception re) {
-                                Log.e(TAG,"",re);
+                    }
+                } else {
+                    synchronized (mConnection) {
+                        try {
+                            if (mService == null) {
+                                if (VDBG) Log.d(TAG,"Binding service...");
+                                doBind();
                             }
+                        } catch (Exception re) {
+                            Log.e(TAG,"",re);
                         }
                     }
                 }
-        };
+            }
+      };
 
     /**
      * Create a BluetoothAvrcpController proxy object for interacting with the local
@@ -223,6 +281,104 @@
         if (mService == null) Log.w(TAG, "Proxy not attached to service");
     }
 
+    /**
+     * Gets the player application settings.
+     *
+     * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
+     */
+    public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getPlayerSettings");
+        BluetoothAvrcpPlayerSettings settings = null;
+        if (mService != null && isEnabled()) {
+            try {
+                settings = mService.getPlayerSettings(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
+                return null;
+            }
+        }
+        return settings;
+    }
+
+    /**
+     * Gets the metadata for the current track.
+     *
+     * This should be usually called when application UI needs to be updated, eg. when the track
+     * changes or immediately after connecting and getting the current state.
+     * @return the {@link MediaMetadata} or {@link null} if there is an error.
+     */
+    public MediaMetadata getMetadata(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getMetadata");
+        MediaMetadata metadata = null;
+        if (mService != null && isEnabled()) {
+            try {
+                metadata = mService.getMetadata(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
+                return null;
+            }
+        }
+        return metadata;
+    }
+
+    /**
+     * Gets the playback state for current track.
+     *
+     * When the application is first connecting it can use current track state to get playback info.
+     * For all further updates it should listen to notifications.
+     * @return the {@link PlaybackState} or {@link null} if there is an error.
+     */
+    public PlaybackState getPlaybackState(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getPlaybackState");
+        PlaybackState playbackState = null;
+        if (mService != null && isEnabled()) {
+            try {
+                playbackState = mService.getPlaybackState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,
+                    "Error talking to BT service in getPlaybackState() " + e);
+                return null;
+            }
+        }
+        return playbackState;
+    }
+
+    /**
+     * Sets the player app setting for current player.
+     * returns true in case setting is supported by remote, false otherwise
+     */
+    public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
+        if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.setPlayerApplicationSetting(plAppSetting);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /*
+     * Send Group Navigation Command to Remote.
+     * possible keycode values: next_grp, previous_grp defined above
+     */
+    public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
+        Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState);
+        if (mService != null && isEnabled()) {
+            try {
+                mService.sendGroupNavigationCmd(device, keyCode, keyState);
+                return;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
+                return;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+    }
+
     private final ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl
index 805fe2c..590fd63 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * Copyright (C) 2015 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.bluetooth;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable BluetoothAvrcpPlayerSettings;
diff --git a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
new file mode 100644
index 0000000..927cb56
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class used to identify settings associated with the player on AG.
+ *
+ * {@hide}
+ */
+public final class BluetoothAvrcpPlayerSettings implements Parcelable {
+    public static final String TAG = "BluetoothAvrcpPlayerSettings";
+
+    /**
+     * Equalizer setting.
+     */
+    public static final int SETTING_EQUALIZER    = 0x01;
+
+    /**
+     * Repeat setting.
+     */
+    public static final int SETTING_REPEAT       = 0x02;
+
+    /**
+     * Shuffle setting.
+     */
+    public static final int SETTING_SHUFFLE      = 0x04;
+
+    /**
+     * Scan mode setting.
+     */
+    public static final int SETTING_SCAN         = 0x08;
+
+    /**
+     * Invalid state.
+     *
+     * Used for returning error codes.
+     */
+    public static final int STATE_INVALID = -1;
+
+    /**
+     * OFF state.
+     *
+     * Denotes a general OFF state. Applies to all settings.
+     */
+    public static final int STATE_OFF = 0x00;
+
+    /**
+     * ON state.
+     *
+     * Applies to {@link SETTING_EQUALIZER}.
+     */
+    public static final int STATE_ON = 0x01;
+
+    /**
+     * Single track repeat.
+     *
+     * Applies only to {@link SETTING_REPEAT}.
+     */
+    public static final int STATE_SINGLE_TRACK = 0x02;
+
+    /**
+     * All track repeat/shuffle.
+     *
+     * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}.
+     */
+    public static final int STATE_ALL_TRACK    = 0x03;
+
+    /**
+     * Group repeat/shuffle.
+     *
+     * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}.
+     */
+    public static final int STATE_GROUP        = 0x04;
+
+    /**
+     * List of supported settings ORed.
+     */
+    private int mSettings;
+
+    /**
+     * Hash map of current capability values.
+     */
+    private Map<Integer, Integer> mSettingsValue = new HashMap<Integer, Integer>();
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mSettings);
+        out.writeInt(mSettingsValue.size());
+        for (int k : mSettingsValue.keySet()) {
+            out.writeInt(k);
+            out.writeInt(mSettingsValue.get(k));
+        }
+    }
+
+    public static final Parcelable.Creator<BluetoothAvrcpPlayerSettings> CREATOR
+            = new Parcelable.Creator<BluetoothAvrcpPlayerSettings>() {
+        public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) {
+            return new BluetoothAvrcpPlayerSettings(in);
+        }
+
+        public BluetoothAvrcpPlayerSettings[] newArray(int size) {
+            return new BluetoothAvrcpPlayerSettings[size];
+        }
+    };
+
+    private BluetoothAvrcpPlayerSettings(Parcel in) {
+        mSettings = in.readInt();
+        int numSettings = in.readInt();
+        for (int i = 0; i < numSettings; i++) {
+            mSettingsValue.put(in.readInt(), in.readInt());
+        }
+    }
+
+    /**
+     * Create a new player settings object.
+     *
+     * @param settings a ORed value of SETTINGS_* defined above.
+     */
+    public BluetoothAvrcpPlayerSettings(int settings) {
+        mSettings = settings;
+    }
+
+    /**
+     * Get the supported settings.
+     *
+     * @return int ORed value of supported settings.
+     */
+    public int getSettings() {
+        return mSettings;
+    }
+
+    /**
+     * Add a setting value.
+     *
+     * The setting must be part of possible settings in {@link getSettings()}.
+     * @param setting setting config.
+     * @param value value for the setting.
+     * @throws IllegalStateException if the setting is not supported.
+     */
+    public void addSettingValue(int setting, int value) {
+        if ((setting & mSettings) == 0) {
+            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
+            throw new IllegalStateException("Setting not supported: " + setting);
+        }
+        mSettingsValue.put(setting, value);
+    }
+
+    /**
+     * Get a setting value.
+     *
+     * The setting must be part of possible settings in {@link getSettings()}.
+     * @param setting setting config.
+     * @return value value for the setting.
+     * @throws IllegalStateException if the setting is not supported.
+     */
+    public int getSettingValue(int setting) {
+        if ((setting & mSettings) == 0) {
+            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
+            throw new IllegalStateException("Setting not supported: " + setting);
+        }
+        Integer i = mSettingsValue.get(setting);
+        if (i == null) return -1;
+        return i;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
old mode 100644
new mode 100755
index 54bf4af..4a38287
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -283,6 +283,8 @@
     public static final int PROFILE_PANU = 4;
     /** @hide */
     public static final int PROFILE_NAP = 5;
+    /** @hide */
+    public static final int PROFILE_A2DP_SINK = 6;
 
     /**
      * Check class bits for possible bluetooth profile support.
@@ -310,6 +312,21 @@
                 default:
                     return false;
             }
+        } else if (profile == PROFILE_A2DP_SINK) {
+            if (hasService(Service.CAPTURE)) {
+                return true;
+            }
+            // By the A2DP spec, srcs must indicate the CAPTURE service.
+            // However if some device that do not, we try to
+            // match on some other class bits.
+            switch (getDeviceClass()) {
+                case Device.AUDIO_VIDEO_HIFI_AUDIO:
+                case Device.AUDIO_VIDEO_SET_TOP_BOX:
+                case Device.AUDIO_VIDEO_VCR :
+                    return true;
+                default:
+                    return false;
+            }
         } else if (profile == PROFILE_HEADSET) {
             // The render service class is required by the spec for HFP, so is a
             // pretty good signal
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index cd5c205..f43fb30 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -879,18 +879,16 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
-     * @param hash - Simple Secure pairing hash
-     * @param randomizer - The random key obtained using OOB
+     * @param transport - Transport to use
+     * @param oobData - Out Of Band data
      * @return false on immediate error, true if bonding will begin
      *
      * @hide
      */
-    public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) {
-        //TODO(BT)
-        /*
+    public boolean createBondOutOfBand(int transport, OobData oobData) {
         try {
-            return sService.createBondOutOfBand(this, hash, randomizer);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}*/
+            return sService.createBondOutOfBand(this, transport, oobData);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
         return false;
     }
 
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 66f3418..74cb0f6 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -20,6 +20,7 @@
 import android.bluetooth.IBluetoothStateChangeCallback;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.OobData;
 import android.os.ParcelUuid;
 import android.os.ParcelFileDescriptor;
 
@@ -56,6 +57,7 @@
 
     BluetoothDevice[] getBondedDevices();
     boolean createBond(in BluetoothDevice device, in int transport);
+    boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData);
     boolean cancelBondProcess(in BluetoothDevice device);
     boolean removeBond(in BluetoothDevice device);
     int getBondState(in BluetoothDevice device);
diff --git a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
old mode 100644
new mode 100755
index b7c6476..d1458246
--- a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
@@ -31,4 +31,7 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     BluetoothAudioConfig getAudioConfig(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device, int priority);
+    int getPriority(in BluetoothDevice device);
+    boolean isA2dpPlaying(in BluetoothDevice device);
 }
diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
index f917a50..f1288d0 100644
--- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
+++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
@@ -16,7 +16,10 @@
 
 package android.bluetooth;
 
+import android.bluetooth.BluetoothAvrcpPlayerSettings;
 import android.bluetooth.BluetoothDevice;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
 
 /**
  * APIs for Bluetooth AVRCP controller service
@@ -28,4 +31,9 @@
     List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
     int getConnectionState(in BluetoothDevice device);
     void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState);
+    BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device);
+    MediaMetadata getMetadata(in BluetoothDevice device);
+    PlaybackState getPlaybackState(in BluetoothDevice device);
+    boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting);
+    void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState);
 }
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/core/java/android/bluetooth/OobData.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to core/java/android/bluetooth/OobData.aidl
index 805fe2c..d831c64 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/core/java/android/bluetooth/OobData.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.bluetooth;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable OobData;
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
new file mode 100644
index 0000000..01f72ef
--- /dev/null
+++ b/core/java/android/bluetooth/OobData.java
@@ -0,0 +1,63 @@
+/*
+ * 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.util.Log;
+
+/**
+ * Out Of Band Data for Bluetooth device.
+ */
+public class OobData implements Parcelable {
+    private byte[] securityManagerTk;
+
+    public byte[] getSecurityManagerTk() {
+        return securityManagerTk;
+    }
+
+    public void setSecurityManagerTk(byte[] securityManagerTk) {
+        this.securityManagerTk = securityManagerTk;
+    }
+
+    public OobData() { }
+
+    private OobData(Parcel in) {
+        securityManagerTk = in.createByteArray();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(securityManagerTk);
+    }
+
+    public static final Parcelable.Creator<OobData> CREATOR
+            = new Parcelable.Creator<OobData>() {
+        public OobData createFromParcel(Parcel in) {
+            return new OobData(in);
+        }
+
+        public OobData[] newArray(int size) {
+            return new OobData[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index d12595f..4135487 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetFileDescriptor;
+import android.database.CrossProcessCursorWrapper;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
@@ -32,27 +33,34 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import dalvik.system.CloseGuard;
 
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * The public interface object used to interact with a {@link ContentProvider}. This is obtained by
- * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
- * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
- * no longer needed and can be killed to free up resources.
- *
- * <p>Note that you should generally create a new ContentProviderClient instance
- * for each thread that will be performing operations.  Unlike
+ * The public interface object used to interact with a specific
+ * {@link ContentProvider}.
+ * <p>
+ * Instances can be obtained by calling
+ * {@link ContentResolver#acquireContentProviderClient} or
+ * {@link ContentResolver#acquireUnstableContentProviderClient}. Instances must
+ * be released using {@link #close()} in order to indicate to the system that
+ * the underlying {@link ContentProvider} is no longer needed and can be killed
+ * to free up resources.
+ * <p>
+ * Note that you should generally create a new ContentProviderClient instance
+ * for each thread that will be performing operations. Unlike
  * {@link ContentResolver}, the methods here such as {@link #query} and
- * {@link #openFile} are not thread safe -- you must not call
- * {@link #release()} on the ContentProviderClient those calls are made from
- * until you are finished with the data they have returned.
+ * {@link #openFile} are not thread safe -- you must not call {@link #close()}
+ * on the ContentProviderClient those calls are made from until you are finished
+ * with the data they have returned.
  */
-public class ContentProviderClient {
+public class ContentProviderClient implements AutoCloseable {
     private static final String TAG = "ContentProviderClient";
 
     @GuardedBy("ContentProviderClient.class")
@@ -63,22 +71,23 @@
     private final String mPackageName;
     private final boolean mStable;
 
-    private final CloseGuard mGuard = CloseGuard.get();
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
     private long mAnrTimeout;
     private NotRespondingRunnable mAnrRunnable;
 
-    private boolean mReleased;
-
     /** {@hide} */
-    ContentProviderClient(
+    @VisibleForTesting
+    public ContentProviderClient(
             ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
         mContentResolver = contentResolver;
         mContentProvider = contentProvider;
         mPackageName = contentResolver.mPackageName;
+
         mStable = stable;
 
-        mGuard.open("release");
+        mCloseGuard.open("close");
     }
 
     /** {@hide} */
@@ -133,8 +142,19 @@
                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
                 cancellationSignal.setRemote(remoteCancellationSignal);
             }
-            return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
-                    sortOrder, remoteCancellationSignal);
+            final Cursor cursor = mContentProvider.query(mPackageName, url, projection, selection,
+                    selectionArgs, sortOrder, remoteCancellationSignal);
+            if (cursor == null) {
+                return null;
+            }
+
+            if ("com.google.android.gms".equals(mPackageName)
+                    || "com.google.android.syncadapters.contacts".equals(mPackageName)) {
+                // They're casting to a concrete subclass, sigh
+                return cursor;
+            } else {
+                return new CursorWrapperInner(cursor);
+            }
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
@@ -446,29 +466,42 @@
     }
 
     /**
-     * Call this to indicate to the system that the associated {@link ContentProvider} is no
-     * longer needed by this {@link ContentProviderClient}.
-     * @return true if this was release, false if it was already released
+     * Closes this client connection, indicating to the system that the
+     * underlying {@link ContentProvider} is no longer needed.
      */
+    @Override
+    public void close() {
+        closeInternal();
+    }
+
+    /**
+     * @deprecated replaced by {@link #close()}.
+     */
+    @Deprecated
     public boolean release() {
-        synchronized (this) {
-            if (mReleased) {
-                throw new IllegalStateException("Already released");
-            }
-            mReleased = true;
-            mGuard.close();
+        return closeInternal();
+    }
+
+    private boolean closeInternal() {
+        mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
             if (mStable) {
                 return mContentResolver.releaseProvider(mContentProvider);
             } else {
                 return mContentResolver.releaseUnstableProvider(mContentProvider);
             }
+        } else {
+            return false;
         }
     }
 
     @Override
     protected void finalize() throws Throwable {
-        if (mGuard != null) {
-            mGuard.warnIfOpen();
+        try {
+            mCloseGuard.warnIfOpen();
+            close();
+        } finally {
+            super.finalize();
         }
     }
 
@@ -502,4 +535,29 @@
             mContentResolver.appNotRespondingViaProvider(mContentProvider);
         }
     }
+
+    private final class CursorWrapperInner extends CrossProcessCursorWrapper {
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+
+        CursorWrapperInner(Cursor cursor) {
+            super(cursor);
+            mCloseGuard.open("close");
+        }
+
+        @Override
+        public void close() {
+            mCloseGuard.close();
+            super.close();
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                mCloseGuard.warnIfOpen();
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+    }
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9d0ebc2..684a85e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
@@ -47,11 +49,11 @@
 import android.util.EventLog;
 import android.util.Log;
 
-import dalvik.system.CloseGuard;
-
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
+import dalvik.system.CloseGuard;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -59,9 +61,9 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * This class provides applications access to the content model.
@@ -514,8 +516,9 @@
             maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
 
             // Wrap the cursor object into CursorWrapperInner object.
-            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
-                    stableProvider != null ? stableProvider : acquireProvider(uri));
+            final IContentProvider provider = (stableProvider != null) ? stableProvider
+                    : acquireProvider(uri);
+            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
             stableProvider = null;
             qCursor = null;
             return wrapper;
@@ -1609,7 +1612,7 @@
 
     /** @hide - designated user version */
     public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
-            ContentObserver observer, int userHandle) {
+            ContentObserver observer, @UserIdInt int userHandle) {
         try {
             getContentService().registerContentObserver(uri, notifyForDescendents,
                     observer.getContentObserver(), userHandle);
@@ -1683,7 +1686,7 @@
      * @hide
      */
     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
-            int userHandle) {
+            @UserIdInt int userHandle) {
         try {
             getContentService().notifyChange(
                     uri, observer == null ? null : observer.getContentObserver(),
@@ -1824,7 +1827,7 @@
      * @see #requestSync(Account, String, Bundle)
      * @hide
      */
-    public static void requestSyncAsUser(Account account, String authority, int userId,
+    public static void requestSyncAsUser(Account account, String authority, @UserIdInt int userId,
             Bundle extras) {
         if (extras == null) {
             throw new IllegalArgumentException("Must specify extras.");
@@ -1921,7 +1924,7 @@
      * @see #cancelSync(Account, String)
      * @hide
      */
-    public static void cancelSyncAsUser(Account account, String authority, int userId) {
+    public static void cancelSyncAsUser(Account account, String authority, @UserIdInt int userId) {
         try {
             getContentService().cancelSyncAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
@@ -1944,7 +1947,7 @@
      * @see #getSyncAdapterTypes()
      * @hide
      */
-    public static SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
+    public static SyncAdapterType[] getSyncAdapterTypesAsUser(@UserIdInt int userId) {
         try {
             return getContentService().getSyncAdapterTypesAsUser(userId);
         } catch (RemoteException e) {
@@ -1956,8 +1959,9 @@
      * @hide
      * Returns the package names of syncadapters that match a given user and authority.
      */
+    @TestApi
     public static String[] getSyncAdapterPackagesForAuthorityAsUser(String authority,
-            int userId) {
+            @UserIdInt int userId) {
         try {
             return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
         } catch (RemoteException e) {
@@ -1987,7 +1991,7 @@
      * @hide
      */
     public static boolean getSyncAutomaticallyAsUser(Account account, String authority,
-            int userId) {
+            @UserIdInt int userId) {
         try {
             return getContentService().getSyncAutomaticallyAsUser(account, authority, userId);
         } catch (RemoteException e) {
@@ -2013,7 +2017,7 @@
      * @hide
      */
     public static void setSyncAutomaticallyAsUser(Account account, String authority, boolean sync,
-            int userId) {
+            @UserIdInt int userId) {
         try {
             getContentService().setSyncAutomaticallyAsUser(account, authority, sync, userId);
         } catch (RemoteException e) {
@@ -2172,7 +2176,8 @@
      * @see #getIsSyncable(Account, String)
      * @hide
      */
-    public static int getIsSyncableAsUser(Account account, String authority, int userId) {
+    public static int getIsSyncableAsUser(Account account, String authority,
+            @UserIdInt int userId) {
         try {
             return getContentService().getIsSyncableAsUser(account, authority, userId);
         } catch (RemoteException e) {
@@ -2215,7 +2220,7 @@
      * @see #getMasterSyncAutomatically()
      * @hide
      */
-    public static boolean getMasterSyncAutomaticallyAsUser(int userId) {
+    public static boolean getMasterSyncAutomaticallyAsUser(@UserIdInt int userId) {
         try {
             return getContentService().getMasterSyncAutomaticallyAsUser(userId);
         } catch (RemoteException e) {
@@ -2239,7 +2244,7 @@
      * @see #setMasterSyncAutomatically(boolean)
      * @hide
      */
-    public static void setMasterSyncAutomaticallyAsUser(boolean sync, int userId) {
+    public static void setMasterSyncAutomaticallyAsUser(boolean sync, @UserIdInt int userId) {
         try {
             getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
         } catch (RemoteException e) {
@@ -2319,7 +2324,7 @@
      * @see #getCurrentSyncs()
      * @hide
      */
-    public static List<SyncInfo> getCurrentSyncsAsUser(int userId) {
+    public static List<SyncInfo> getCurrentSyncsAsUser(@UserIdInt int userId) {
         try {
             return getContentService().getCurrentSyncsAsUser(userId);
         } catch (RemoteException e) {
@@ -2347,7 +2352,7 @@
      * @hide
      */
     public static SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
-            int userId) {
+            @UserIdInt int userId) {
         try {
             return getContentService().getSyncStatusAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
@@ -2371,7 +2376,8 @@
      * @see #requestSync(Account, String, Bundle)
      * @hide
      */
-    public static boolean isSyncPendingAsUser(Account account, String authority, int userId) {
+    public static boolean isSyncPendingAsUser(Account account, String authority,
+            @UserIdInt int userId) {
         try {
             return getContentService().isSyncPendingAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
@@ -2503,41 +2509,31 @@
 
     private final class CursorWrapperInner extends CrossProcessCursorWrapper {
         private final IContentProvider mContentProvider;
-        public static final String TAG="CursorWrapperInner";
+        private final AtomicBoolean mProviderReleased = new AtomicBoolean();
 
         private final CloseGuard mCloseGuard = CloseGuard.get();
-        private boolean mProviderReleased;
 
-        CursorWrapperInner(Cursor cursor, IContentProvider icp) {
+        CursorWrapperInner(Cursor cursor, IContentProvider contentProvider) {
             super(cursor);
-            mContentProvider = icp;
+            mContentProvider = contentProvider;
             mCloseGuard.open("close");
         }
 
         @Override
         public void close() {
+            mCloseGuard.close();
             super.close();
-            ContentResolver.this.releaseProvider(mContentProvider);
-            mProviderReleased = true;
 
-            if (mCloseGuard != null) {
-                mCloseGuard.close();
+            if (mProviderReleased.compareAndSet(false, true)) {
+                ContentResolver.this.releaseProvider(mContentProvider);
             }
         }
 
         @Override
         protected void finalize() throws Throwable {
             try {
-                if (mCloseGuard != null) {
-                    mCloseGuard.warnIfOpen();
-                }
-
-                if (!mProviderReleased && mContentProvider != null) {
-                    // Even though we are using CloseGuard, log this anyway so that
-                    // application developers always see the message in the log.
-                    Log.w(TAG, "Cursor finalized without prior close()");
-                    ContentResolver.this.releaseProvider(mContentProvider);
-                }
+                mCloseGuard.warnIfOpen();
+                close();
             } finally {
                 super.finalize();
             }
@@ -2546,7 +2542,7 @@
 
     private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
         private final IContentProvider mContentProvider;
-        private boolean mProviderReleased;
+        private final AtomicBoolean mProviderReleased = new AtomicBoolean();
 
         ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
             super(pfd);
@@ -2555,9 +2551,8 @@
 
         @Override
         public void releaseResources() {
-            if (!mProviderReleased) {
+            if (mProviderReleased.compareAndSet(false, true)) {
                 ContentResolver.this.releaseProvider(mContentProvider);
-                mProviderReleased = true;
             }
         }
     }
@@ -2584,7 +2579,9 @@
 
     private static IContentService sContentService;
     private final Context mContext;
+
     final String mPackageName;
+
     private static final String TAG = "ContentResolver";
 
     /** @hide */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 67bdad5..48d0196 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -30,6 +30,8 @@
 import android.annotation.StyleRes;
 import android.annotation.StyleableRes;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -327,6 +329,16 @@
     public static final int BIND_NOT_VISIBLE = 0x40000000;
 
     /**
+     * Flag for {@link #bindService}: The service being bound is an
+     * {@link android.R.attr#isolatedProcess isolated},
+     * {@link android.R.attr#externalService external} service.  This binds the service into the
+     * calling application's package, rather than the package in which the service is declared.
+     * @hide
+     */
+    @SystemApi
+    public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
+
+    /**
      * Returns an AssetManager instance for the application's package.
      * <p>
      * <strong>Note:</strong> Implementations of this method should return
@@ -2548,6 +2560,7 @@
             NETWORK_STATS_SERVICE,
             //@hide: NETWORK_POLICY_SERVICE,
             WIFI_SERVICE,
+            WIFI_NAN_SERVICE,
             WIFI_PASSPOINT_SERVICE,
             WIFI_P2P_SERVICE,
             WIFI_SCANNING_SERVICE,
@@ -3010,6 +3023,17 @@
     public static final String WIFI_P2P_SERVICE = "wifip2p";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.net.wifi.nan.WifiNanManager} for handling management of
+     * Wi-Fi NAN discovery and connections.
+     *
+     * @see #getSystemService
+     * @see android.net.wifi.nan.WifiNanManager
+     * @hide PROPOSED_NAN_API
+     */
+    public static final String WIFI_NAN_SERVICE = "wifinan";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.net.wifi.WifiScanner} for scanning the wifi universe
      *
@@ -3967,7 +3991,8 @@
      *
      * @hide
      */
-    public abstract int getUserId();
+    @TestApi
+    public abstract @UserIdInt int getUserId();
 
     /**
      * Return a new Context object for the current Context but whose resources
@@ -4018,13 +4043,16 @@
      * Because device-encrypted data is available before user authentication,
      * you should carefully consider what data you store using this Context.
      * <p>
+     * If the underlying device does not have the ability to store
+     * device-encrypted and credential-encrypted data using different keys, then
+     * both storage areas will become available at the same time. They remain
+     * two distinct storage areas, and only the window of availability changes.
+     * <p>
      * Each call to this method returns a new instance of a Context object;
      * Context objects are not shared, however common state (ClassLoader, other
      * Resources for the same configuration) may be so the Context itself can be
      * fairly lightweight.
      *
-     * @return new Context or {@code null} if device-encrypted storage is not
-     *         supported or available on this device.
      * @see #isDeviceEncryptedStorage()
      */
     public abstract Context createDeviceEncryptedStorageContext();
@@ -4038,6 +4066,11 @@
      * <em>only after</em> the user has entered their credentials (such as a
      * lock pattern or PIN).
      * <p>
+     * If the underlying device does not have the ability to store
+     * device-encrypted and credential-encrypted data using different keys, then
+     * both storage areas will become available at the same time. They remain
+     * two distinct storage areas, and only the window of availability changes.
+     * <p>
      * Each call to this method returns a new instance of a Context object;
      * Context objects are not shared, however common state (ClassLoader, other
      * Resources for the same configuration) may be so the Context itself can be
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f3ca10..c627436 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2537,6 +2537,16 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
 
+   /**
+     * Broadcast Action:  The "Picture-in-picture (PIP) Button" was pressed.
+     * Includes a single extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
+     * caused the broadcast.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PICTURE_IN_PICTURE_BUTTON =
+            "android.intent.action.PICTURE_IN_PICTURE_BUTTON";
+
     /**
      * Broadcast Action:  The "Camera Button" was pressed.  Includes a single
      * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
@@ -3034,6 +3044,24 @@
     public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
 
     /**
+     * Broadcast Action: Sent when media resource is granted.
+     * <p>
+     * {@link #EXTRA_PACKAGES} specifies the packages on the process holding the media resource
+     * granted.
+     * </p>
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     * <p class="note">
+     * This requires {@link android.Manifest.permission#RECEIVE_MEDIA_RESOURCE_USAGE} permission.
+     * </p>
+     *
+     * @hide
+     */
+    public static final String ACTION_MEDIA_RESOURCE_GRANTED =
+            "android.intent.action.MEDIA_RESOURCE_GRANTED";
+
+    /**
      * Activity Action: Allow the user to select and return one or more existing
      * documents. When invoked, the system will display the various
      * {@link DocumentsProvider} instances installed on the device, letting the
@@ -3604,6 +3632,15 @@
     public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";
 
     /**
+     * An int representing the task id to be retrieved. This is used when a launch from recents is
+     * intercepted by another action such as credentials confirmation to remember which task should
+     * be resumed when complete.
+     *
+     * @hide
+     */
+    public static final String EXTRA_TASK_ID = "android.intent.extra.TASK_ID";
+
+    /**
      * An Intent[] describing additional, alternate choices you would like shown with
      * {@link #ACTION_CHOOSER}.
      *
@@ -4068,6 +4105,34 @@
      * Optional boolean extra indicating whether quiet mode has been switched on or off.
      */
     public static final String EXTRA_QUIET_MODE = "android.intent.extra.QUIET_MODE";
+
+    /**
+     * Used as an int extra field in {@link #ACTION_MEDIA_RESOURCE_GRANTED}
+     * intents to specify the resource type granted. Possible values are
+     * {@link #EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC} or
+     * {@link #EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_MEDIA_RESOURCE_TYPE =
+            "android.intent.extra.MEDIA_RESOURCE_TYPE";
+
+    /**
+     * Used as an int value for {@link #EXTRA_MEDIA_RESOURCE_TYPE}
+     * to represent that a video codec is allowed to use.
+     *
+     * @hide
+     */
+    public static final int EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC = 0;
+
+    /**
+     * Used as an int value for {@link #EXTRA_MEDIA_RESOURCE_TYPE}
+     * to represent that a audio codec is allowed to use.
+     *
+     * @hide
+     */
+    public static final int EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC = 1;
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
@@ -4169,11 +4234,18 @@
 
     /**
      * Internal flag used to indicate that a system component has done their
-     * homework and verified their encryption-aware behavior.
+     * homework and verified that they correctly handle packages and components
+     * that come and go over time. In particular:
+     * <ul>
+     * <li>Apps installed on external storage, which will appear to be
+     * uninstalled while the the device is ejected.
+     * <li>Apps with encryption unaware components, which will appear to not
+     * exist while the device is locked.
+     * </ul>
      *
      * @hide
      */
-    public static final int FLAG_DEBUG_ENCRYPTION_TRIAGED = 0x00000100;
+    public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100;
 
     /**
      * If set, the new activity is not kept in the history stack.  As soon as
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 326735e..dedf07f5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -281,6 +281,29 @@
      * {@see android.app.Activity#setVrMode(boolean)}.
      */
     public static final int FLAG_ENABLE_VR_MODE = 0x8000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity is resizeable to any dimension.
+     * See {@link android.R.attr#resizeableActivity}.
+     * @hide
+     */
+    public static final int FLAG_RESIZEABLE = 0x10000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity is supports picture-in-picture form of
+     * multi-window mode. See {@link android.R.attr#supportsPictureInPicture}.
+     * @hide
+     */
+    public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x20000;
+
+    /**
+     * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
+     * in a task/stack whose activities are normally not focusable.
+     * See android.R.attr#alwaysFocusable.
+     * @hide
+     */
+    public static final int FLAG_ALWAYS_FOCUSABLE = 0x40000;
+
     /**
      * @hide Bit in {@link #flags}: If set, this component will only be seen
      * by the system user.  Only works with broadcast receivers.  Set from the
@@ -670,20 +693,6 @@
      */
     public String parentActivityName;
 
-    /**
-     * Value indicating if the activity is resizeable to any dimension.
-     * See {@link android.R.attr#resizeableActivity}.
-     * @hide
-     */
-    public boolean resizeable;
-
-    /**
-     * Value indicating if the activity is supports picture-in-picture form of multi-window mode.
-     * See {@link android.R.attr#supportsPictureInPicture}.
-     * @hide
-     */
-    public boolean supportsPip;
-
     /** @hide */
     public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
     /** @hide */
@@ -735,8 +744,6 @@
         uiOptions = orig.uiOptions;
         parentActivityName = orig.parentActivityName;
         maxRecents = orig.maxRecents;
-        resizeable = orig.resizeable;
-        supportsPip = orig.supportsPip;
         lockTaskLaunchMode = orig.lockTaskLaunchMode;
         layout = orig.layout;
     }
@@ -791,7 +798,6 @@
             pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
         }
         if ((flags&DUMP_FLAG_DETAILS) != 0) {
-            pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
             pw.println(prefix + "lockTaskLaunchMode="
                     + lockTaskLaunchModeToString(lockTaskLaunchMode));
         }
@@ -829,8 +835,6 @@
         dest.writeString(parentActivityName);
         dest.writeInt(persistableMode);
         dest.writeInt(maxRecents);
-        dest.writeInt(resizeable ? 1 : 0);
-        dest.writeInt(supportsPip ? 1 : 0);
         dest.writeInt(lockTaskLaunchMode);
         if (layout != null) {
             dest.writeInt(1);
@@ -871,8 +875,6 @@
         parentActivityName = source.readString();
         persistableMode = source.readInt();
         maxRecents = source.readInt();
-        resizeable = (source.readInt() == 1);
-        supportsPip = (source.readInt() == 1);
         lockTaskLaunchMode = source.readInt();
         if (source.readInt() == 1) {
             layout = new Layout(source);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index fe279d1..0168908 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.TestApi;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
@@ -803,7 +804,12 @@
     public boolean hasRtlSupport() {
         return (flags & FLAG_SUPPORTS_RTL) == FLAG_SUPPORTS_RTL;
     }
-    
+
+    /** {@hide} */
+    public boolean hasCode() {
+        return (flags & FLAG_HAS_CODE) != 0;
+    }
+
     public static class DisplayNameComparator
             implements Comparator<ApplicationInfo> {
         public DisplayNameComparator(PackageManager pm) {
@@ -1069,6 +1075,7 @@
     /**
      * @hide
      */
+    @TestApi
     public boolean isSystemApp() {
         return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
@@ -1076,6 +1083,7 @@
     /**
      * @hide
      */
+    @TestApi
     public boolean isPrivilegedApp() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 39f59554..90a1198 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -64,10 +64,8 @@
     void checkPackageStartable(String packageName, int userId);
     boolean isPackageAvailable(String packageName, int userId);
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
-    int getPackageUid(String packageName, int userId);
-    int getPackageUidEtc(String packageName, int flags, int userId);
-    int[] getPackageGids(String packageName, int userId);
-    int[] getPackageGidsEtc(String packageName, int flags, int userId);
+    int getPackageUid(String packageName, int flags, int userId);
+    int[] getPackageGids(String packageName, int flags, int userId);
 
     String[] currentToCanonicalPackageNames(in String[] names);
     String[] canonicalToCurrentPackageNames(in String[] names);
@@ -296,6 +294,8 @@
     void restoreDefaultApps(in byte[] backup, int userId);
     byte[] getIntentFilterVerificationBackup(int userId);
     void restoreIntentFilterVerification(in byte[] backup, int userId);
+    byte[] getPermissionGrantBackup(int userId);
+    void restorePermissionGrants(in byte[] backup, int userId);
 
     /**
      * Report the set of 'Home' activity candidates, plus (if any) which of them
diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java
deleted file mode 100644
index e7dc764..0000000
--- a/core/java/android/content/pm/ManifestDigest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import com.android.internal.util.HexDump;
-
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Slog;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import libcore.io.IoUtils;
-
-/**
- * Represents the manifest digest for a package. This is suitable for comparison
- * of two packages to know whether the manifests are identical.
- *
- * @hide
- */
-@SystemApi
-public class ManifestDigest implements Parcelable {
-    private static final String TAG = "ManifestDigest";
-
-    /** The digest of the manifest in our preferred order. */
-    private final byte[] mDigest;
-
-    /** What we print out first when toString() is called. */
-    private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest=";
-
-    /** Digest algorithm to use. */
-    private static final String DIGEST_ALGORITHM = "SHA-256";
-
-    ManifestDigest(byte[] digest) {
-        mDigest = digest;
-    }
-
-    private ManifestDigest(Parcel source) {
-        mDigest = source.createByteArray();
-    }
-
-    static ManifestDigest fromInputStream(InputStream fileIs) {
-        if (fileIs == null) {
-            return null;
-        }
-
-        final MessageDigest md;
-        try {
-            md = MessageDigest.getInstance(DIGEST_ALGORITHM);
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e);
-        }
-
-        final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md);
-        try {
-            byte[] readBuffer = new byte[8192];
-            while (dis.read(readBuffer, 0, readBuffer.length) != -1) {
-                // not using
-            }
-        } catch (IOException e) {
-            Slog.w(TAG, "Could not read manifest");
-            return null;
-        } finally {
-            IoUtils.closeQuietly(dis);
-        }
-
-        final byte[] digest = md.digest();
-        return new ManifestDigest(digest);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof ManifestDigest)) {
-            return false;
-        }
-
-        final ManifestDigest other = (ManifestDigest) o;
-
-        return this == other || Arrays.equals(mDigest, other.mDigest);
-    }
-
-    @Override
-    public int hashCode() {
-        return Arrays.hashCode(mDigest);
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder(TO_STRING_PREFIX.length()
-                + (mDigest.length * 3) + 1);
-
-        sb.append(TO_STRING_PREFIX);
-
-        final int N = mDigest.length;
-        for (int i = 0; i < N; i++) {
-            final byte b = mDigest[i];
-            HexDump.appendByteAsHex(sb, b, false);
-            sb.append(',');
-        }
-        sb.append('}');
-
-        return sb.toString();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByteArray(mDigest);
-    }
-
-    public static final Parcelable.Creator<ManifestDigest> CREATOR
-            = new Parcelable.Creator<ManifestDigest>() {
-        public ManifestDigest createFromParcel(Parcel source) {
-            return new ManifestDigest(source);
-        }
-
-        public ManifestDigest[] newArray(int size) {
-            return new ManifestDigest[size];
-        }
-    };
-
-}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 69f508e..ba4d14c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -27,6 +27,8 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.StringRes;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.annotation.XmlRes;
 import android.app.PackageDeleteObserver;
 import android.app.PackageInstallObserver;
@@ -48,6 +50,7 @@
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 import android.util.AndroidException;
+import android.util.Log;
 
 import com.android.internal.util.ArrayUtils;
 
@@ -63,6 +66,7 @@
  * You can find this class through {@link Context#getPackageManager}.
  */
 public abstract class PackageManager {
+    private static final String TAG = "PackageManager";
 
     /**
      * This exception is thrown when a given package, application, or component
@@ -93,6 +97,110 @@
     }
 
     /**
+     * As a guiding principle:
+     * <p>
+     * {@code GET_} flags are used to request additional data that may have been
+     * elided to save wire space.
+     * <p>
+     * {@code MATCH_} flags are used to include components or packages that
+     * would have otherwise been omitted from a result set by current system
+     * state.
+     */
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_ACTIVITIES,
+            GET_CONFIGURATIONS,
+            GET_GIDS,
+            GET_INSTRUMENTATION,
+            GET_INTENT_FILTERS,
+            GET_META_DATA,
+            GET_PERMISSIONS,
+            GET_PROVIDERS,
+            GET_RECEIVERS,
+            GET_SERVICES,
+            GET_SHARED_LIBRARY_FILES,
+            GET_SIGNATURES,
+            GET_URI_PERMISSION_PATTERNS,
+            MATCH_UNINSTALLED_PACKAGES,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_SYSTEM_ONLY,
+            MATCH_DEBUG_TRIAGED_MISSING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PackageInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_META_DATA,
+            GET_SHARED_LIBRARY_FILES,
+            MATCH_UNINSTALLED_PACKAGES,
+            MATCH_SYSTEM_ONLY,
+            MATCH_DEBUG_TRIAGED_MISSING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApplicationInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_META_DATA,
+            GET_SHARED_LIBRARY_FILES,
+            MATCH_ALL,
+            MATCH_DEBUG_TRIAGED_MISSING,
+            MATCH_DEFAULT_ONLY,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_ENCRYPTION_AWARE,
+            MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+            MATCH_ENCRYPTION_UNAWARE,
+            MATCH_SYSTEM_ONLY,
+            MATCH_UNINSTALLED_PACKAGES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComponentInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_META_DATA,
+            GET_RESOLVED_FILTER,
+            GET_SHARED_LIBRARY_FILES,
+            MATCH_ALL,
+            MATCH_DEBUG_TRIAGED_MISSING,
+            MATCH_DISABLED_COMPONENTS,
+            MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+            MATCH_DEFAULT_ONLY,
+            MATCH_ENCRYPTION_AWARE,
+            MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+            MATCH_ENCRYPTION_UNAWARE,
+            MATCH_SYSTEM_ONLY,
+            MATCH_UNINSTALLED_PACKAGES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ResolveInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_META_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_META_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionGroupInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, value = {
+            GET_META_DATA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstrumentationInfoFlags {}
+
+    /**
      * {@link PackageInfo} flag: return information about
      * activities in the package in {@link PackageInfo#activities}.
      */
@@ -161,9 +269,15 @@
     public static final int GET_GIDS                    = 0x00000100;
 
     /**
+     * @deprecated replaced with {@link #MATCH_DISABLED_COMPONENTS}
+     */
+    @Deprecated
+    public static final int GET_DISABLED_COMPONENTS = 0x00000200;
+
+    /**
      * {@link PackageInfo} flag: include disabled components in the returned info.
      */
-    public static final int GET_DISABLED_COMPONENTS     = 0x00000200;
+    public static final int MATCH_DISABLED_COMPONENTS = 0x00000200;
 
     /**
      * {@link ApplicationInfo} flag: return the
@@ -190,6 +304,12 @@
     public static final int GET_PERMISSIONS               = 0x00001000;
 
     /**
+     * @deprecated replaced with {@link #MATCH_UNINSTALLED_PACKAGES}
+     */
+    @Deprecated
+    public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
+
+    /**
      * Flag parameter to retrieve some information about all applications (even
      * uninstalled ones) which have data directories. This state could have
      * resulted if applications have been deleted with flag
@@ -199,7 +319,7 @@
      * Note: this flag may cause less information about currently installed
      * applications to be returned.
      */
-    public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
+    public static final int MATCH_UNINSTALLED_PACKAGES = 0x00002000;
 
     /**
      * {@link PackageInfo} flag: return information about
@@ -211,12 +331,18 @@
     public static final int GET_CONFIGURATIONS = 0x00004000;
 
     /**
+     * @deprecated replaced with {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS}.
+     */
+    @Deprecated
+    public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+
+    /**
      * {@link PackageInfo} flag: include disabled components which are in
      * that state only because of {@link #COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED}
      * in the returned info.  Note that if you set this flag, applications
      * that are in this disabled state will be reported as enabled.
      */
-    public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+    public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
 
     /**
      * Resolution and querying flag: if set, only filters that support the
@@ -227,60 +353,68 @@
     public static final int MATCH_DEFAULT_ONLY  = 0x00010000;
 
     /**
-     * Querying flag: if set and if the platform is doing any filtering of the results, then
-     * the filtering will not happen. This is a synonym for saying that all results should
-     * be returned.
+     * Querying flag: if set and if the platform is doing any filtering of the
+     * results, then the filtering will not happen. This is a synonym for saying
+     * that all results should be returned.
+     * <p>
+     * <em>This flag should be used with extreme care.</em>
      */
     public static final int MATCH_ALL = 0x00020000;
 
     /**
-     * {@link PackageInfo} flag: include only components which are encryption
-     * unaware in the returned info, regardless of the current user state.
+     * Querying flag: include only components which are encryption unaware in
+     * the returned info, regardless of the current user state.
      */
-    public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 0x00040000;
+    public static final int MATCH_ENCRYPTION_UNAWARE = 0x00040000;
 
     /**
-     * {@link PackageInfo} flag: include only components which are encryption
-     * aware in the returned info, regardless of the current user state.
+     * Querying flag: include only components which are encryption aware in the
+     * returned info, regardless of the current user state.
      */
-    public static final int MATCH_ENCRYPTION_AWARE_ONLY = 0x00080000;
+    public static final int MATCH_ENCRYPTION_AWARE = 0x00080000;
 
     /**
-     * {@link PackageInfo} flag: include both encryption aware and unaware
-     * components in the returned info, regardless of the current user state.
+     * Querying flag: include both encryption aware and unaware components in
+     * the returned info, regardless of the current user state.
      */
-    public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE_ONLY
-            | MATCH_ENCRYPTION_UNAWARE_ONLY;
+    public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE
+            | MATCH_ENCRYPTION_UNAWARE;
 
     /**
-     * {@link PackageInfo} flag: include only components from applications that
-     * are marked with {@link ApplicationInfo#FLAG_SYSTEM}.
-     *
-     * @hide
+     * Querying flag: include only components from applications that are marked
+     * with {@link ApplicationInfo#FLAG_SYSTEM}.
      */
     public static final int MATCH_SYSTEM_ONLY = 0x00100000;
 
     /**
-     * {@link PackageInfo} flag: use the default encryption matching behavior
-     * based on user state. Internal flag used to indicate that a system
-     * component has done their homework and verified their encryption-aware
-     * behavior.
+     * Internal flag used to indicate that a system component has done their
+     * homework and verified that they correctly handle packages and components
+     * that come and go over time. In particular:
+     * <ul>
+     * <li>Apps installed on external storage, which will appear to be
+     * uninstalled while the the device is ejected.
+     * <li>Apps with encryption unaware components, which will appear to not
+     * exist while the device is locked.
+     * </ul>
      *
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
      * @hide
      */
-    public static final int MATCH_ENCRYPTION_DEFAULT = 0x10000000;
+    public static final int MATCH_DEBUG_TRIAGED_MISSING = 0x10000000;
 
     /**
-     * Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
-     * when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current
-     * profile will be skipped.
-     * Only activities in the target user can respond to the intent.
+     * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
+     * resolving an intent that matches the {@code CrossProfileIntentFilter},
+     * the current profile will be skipped. Only activities in the target user
+     * can respond to the intent.
+     *
      * @hide
      */
     public static final int SKIP_CURRENT_PROFILE = 0x00000002;
 
     /**
-     * Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
+     * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set:
      * activities in the other profiles can respond to the intent only if no activity with
      * non-negative priority in current profile can respond to the intent.
      * @hide
@@ -387,17 +521,37 @@
      */
     public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4;
 
+    /** @hide */
+    @IntDef(flag = true, value = {
+            INSTALL_FORWARD_LOCK,
+            INSTALL_REPLACE_EXISTING,
+            INSTALL_ALLOW_TEST,
+            INSTALL_EXTERNAL,
+            INSTALL_INTERNAL,
+            INSTALL_FROM_ADB,
+            INSTALL_ALL_USERS,
+            INSTALL_ALLOW_DOWNGRADE,
+            INSTALL_GRANT_RUNTIME_PERMISSIONS,
+            INSTALL_FORCE_VOLUME_UUID,
+            INSTALL_FORCE_PERMISSION_PROMPT,
+            INSTALL_EPHEMERAL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallFlags {}
+
     /**
-     * Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to
-     * indicate that this package should be installed as forward locked, i.e. only the app itself
-     * should have access to its code and non-resource assets.
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * should be installed as forward locked, i.e. only the app itself should
+     * have access to its code and non-resource assets.
+     *
      * @hide
      */
     public static final int INSTALL_FORWARD_LOCK = 0x00000001;
 
     /**
-     * Flag parameter for {@link #installPackage} to indicate that you want to replace an already
-     * installed package, if one exists.
+     * Flag parameter for {@link #installPackage} to indicate that you want to
+     * replace an already installed package, if one exists.
+     *
      * @hide
      */
     public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
@@ -490,170 +644,181 @@
     public static final int DONT_KILL_APP = 0x00000001;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on success.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} on success.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_SUCCEEDED = 1;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package is
-     * already installed.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the package is already installed.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package archive
-     * file is invalid.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the package archive file is invalid.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_INVALID_APK = -2;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the URI passed in
-     * is invalid.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the URI passed in is invalid.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_INVALID_URI = -3;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package manager
-     * service found that the device didn't have enough storage space to install the app.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the package manager service found that
+     * the device didn't have enough storage space to install the app.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if a
-     * package is already installed with the same name.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if a package is already installed with
+     * the same name.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the requested shared user does not exist.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the requested shared user does not
+     * exist.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * a previously installed package of the same name has a different signature
-     * than the new package (and the old package's data was not removed).
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if a previously installed package of the
+     * same name has a different signature than the new package (and the old
+     * package's data was not removed).
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package is requested a shared user which is already installed on the
-     * device and does not have matching signature.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package is requested a shared
+     * user which is already installed on the device and does not have matching
+     * signature.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package uses a shared library that is not available.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package uses a shared library
+     * that is not available.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package uses a shared library that is not available.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package uses a shared library
+     * that is not available.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package failed while optimizing and validating its dex files,
-     * either because there was not enough storage or the validation failed.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package failed while
+     * optimizing and validating its dex files, either because there was not
+     * enough storage or the validation failed.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_DEXOPT = -11;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package failed because the current SDK version is older than
-     * that required by the package.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package failed because the
+     * current SDK version is older than that required by the package.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_OLDER_SDK = -12;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package failed because it contains a content provider with the
-     * same authority as a provider already installed in the system.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package failed because it
+     * contains a content provider with the same authority as a provider already
+     * installed in the system.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package failed because the current SDK version is newer than
-     * that required by the package.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package failed because the
+     * current SDK version is newer than that required by the package.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_NEWER_SDK = -14;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package failed because it has specified that it is a test-only
-     * package and the caller has not supplied the {@link #INSTALL_ALLOW_TEST}
-     * flag.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package failed because it has
+     * specified that it is a test-only package and the caller has not supplied
+     * the {@link #INSTALL_ALLOW_TEST} flag.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_TEST_ONLY = -15;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the package being installed contains native code, but none that is
-     * compatible with the device's CPU_ABI.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the package being installed contains
+     * native code, but none that is compatible with the device's CPU_ABI.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package uses a feature that is not available.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package uses a feature that is
+     * not available.
+     *
      * @hide
      */
     @SystemApi
@@ -661,217 +826,234 @@
 
     // ------ Errors related to sdcard
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * a secure container mount point couldn't be accessed on external media.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if a secure container mount point
+     * couldn't be accessed on external media.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package couldn't be installed in the specified install
-     * location.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package couldn't be installed
+     * in the specified install location.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package couldn't be installed in the specified install
-     * location because the media is not available.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package couldn't be installed
+     * in the specified install location because the media is not available.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package couldn't be installed because the verification timed out.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package couldn't be installed
+     * because the verification timed out.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package couldn't be installed because the verification did not succeed.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package couldn't be installed
+     * because the verification did not succeed.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the package changed from what the calling program expected.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the package changed from what the
+     * calling program expected.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package is assigned a different UID than it previously held.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package is assigned a
+     * different UID than it previously held.
+     *
      * @hide
      */
     public static final int INSTALL_FAILED_UID_CHANGED = -24;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the new package has an older version code than the currently installed package.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the new package has an older version
+     * code than the currently installed package.
+     *
      * @hide
      */
     public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
 
     /**
-     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
-     * the old package has target SDK high enough to support runtime permission and
-     * the new package has target SDK low enough to not support runtime permissions.
+     * Installation return code: this is passed to the
+     * {@link IPackageInstallObserver} if the old package has target SDK high
+     * enough to support runtime permission and the new package has target SDK
+     * low enough to not support runtime permissions.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser was given a path that is not a file, or does not end with the expected
-     * '.apk' extension.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser was given a path that is
+     * not a file, or does not end with the expected '.apk' extension.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser was unable to retrieve the AndroidManifest.xml file.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser was unable to retrieve the
+     * AndroidManifest.xml file.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser encountered an unexpected exception.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser encountered an unexpected
+     * exception.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser did not find any certificates in the .apk.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser did not find any
+     * certificates in the .apk.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser found inconsistent certificates on the files in the .apk.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser found inconsistent
+     * certificates on the files in the .apk.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser encountered a CertificateEncodingException in one of the
-     * files in the .apk.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser encountered a
+     * CertificateEncodingException in one of the files in the .apk.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser encountered a bad or missing package name in the manifest.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser encountered a bad or
+     * missing package name in the manifest.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser encountered a bad shared user id name in the manifest.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser encountered a bad shared
+     * user id name in the manifest.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser encountered some structural problem in the manifest.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser encountered some structural
+     * problem in the manifest.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
 
     /**
-     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the parser did not find any actionable tags (instrumentation or application)
-     * in the manifest.
+     * Installation parse return code: this is passed to the
+     * {@link IPackageInstallObserver} if the parser did not find any actionable
+     * tags (instrumentation or application) in the manifest.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
 
     /**
-     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the system failed to install the package because of system issues.
+     * Installation failed return code: this is passed to the
+     * {@link IPackageInstallObserver} if the system failed to install the
+     * package because of system issues.
+     *
      * @hide
      */
     @SystemApi
     public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
 
     /**
-     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the system failed to install the package because the user is restricted from installing
-     * apps.
+     * Installation failed return code: this is passed to the
+     * {@link IPackageInstallObserver} if the system failed to install the
+     * package because the user is restricted from installing apps.
+     *
      * @hide
      */
     public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
 
     /**
-     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the system failed to install the package because it is attempting to define a
-     * permission that is already defined by some existing package.
+     * Installation failed return code: this is passed to the
+     * {@link IPackageInstallObserver} if the system failed to install the
+     * package because it is attempting to define a permission that is already
+     * defined by some existing package.
+     * <p>
+     * The package name of the app which has already defined the permission is
+     * passed to a {@link PackageInstallObserver}, if any, as the
+     * {@link #EXTRA_FAILURE_EXISTING_PACKAGE} string extra; and the name of the
+     * permission being redefined is passed in the
+     * {@link #EXTRA_FAILURE_EXISTING_PERMISSION} string extra.
      *
-     * <p>The package name of the app which has already defined the permission is passed to
-     * a {@link PackageInstallObserver}, if any, as the {@link #EXTRA_EXISTING_PACKAGE}
-     * string extra; and the name of the permission being redefined is passed in the
-     * {@link #EXTRA_EXISTING_PERMISSION} string extra.
      * @hide
      */
     public static final int INSTALL_FAILED_DUPLICATE_PERMISSION = -112;
 
     /**
-     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
-     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
-     * if the system failed to install the package because its packaged native code did not
-     * match any of the ABIs supported by the system.
+     * Installation failed return code: this is passed to the
+     * {@link IPackageInstallObserver} if the system failed to install the
+     * package because its packaged native code did not match any of the ABIs
+     * supported by the system.
      *
      * @hide
      */
@@ -897,6 +1079,15 @@
      */
     public static final int INSTALL_FAILED_EPHEMERAL_INVALID = -116;
 
+    /** @hide */
+    @IntDef(flag = true, value = {
+            DELETE_KEEP_DATA,
+            DELETE_ALL_USERS,
+            DELETE_SYSTEM_APP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeleteFlags {}
+
     /**
      * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
      * package's data directory.
@@ -926,8 +1117,8 @@
 
     /**
      * Return code for when package deletion succeeds. This is passed to the
-     * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
-     * succeeded in deleting the package.
+     * {@link IPackageDeleteObserver} if the system succeeded in deleting the
+     * package.
      *
      * @hide
      */
@@ -935,8 +1126,8 @@
 
     /**
      * Deletion failed return code: this is passed to the
-     * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
-     * failed to delete the package for an unspecified reason.
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * for an unspecified reason.
      *
      * @hide
      */
@@ -944,9 +1135,8 @@
 
     /**
      * Deletion failed return code: this is passed to the
-     * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
-     * failed to delete the package because it is the active DevicePolicy
-     * manager.
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because it is the active DevicePolicy manager.
      *
      * @hide
      */
@@ -954,8 +1144,8 @@
 
     /**
      * Deletion failed return code: this is passed to the
-     * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
-     * failed to delete the package since the user is restricted.
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * since the user is restricted.
      *
      * @hide
      */
@@ -963,9 +1153,9 @@
 
     /**
      * Deletion failed return code: this is passed to the
-     * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
-     * failed to delete the package because a profile
-     * or device owner has marked the package as uninstallable.
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because a profile or device owner has marked the package as
+     * uninstallable.
      *
      * @hide
      */
@@ -975,8 +1165,7 @@
     public static final int DELETE_FAILED_ABORTED = -5;
 
     /**
-     * Return code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} when the
+     * Return code that is passed to the {@link IPackageMoveObserver} when the
      * package has been successfully moved by the system.
      *
      * @hide
@@ -984,59 +1173,57 @@
     public static final int MOVE_SUCCEEDED = -100;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
-     * when the package hasn't been successfully moved by the system
-     * because of insufficient memory on specified media.
+     * Error code that is passed to the {@link IPackageMoveObserver} when the
+     * package hasn't been successfully moved by the system because of
+     * insufficient memory on specified media.
+     *
      * @hide
      */
     public static final int MOVE_FAILED_INSUFFICIENT_STORAGE = -1;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
-     * if the specified package doesn't exist.
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package doesn't exist.
+     *
      * @hide
      */
     public static final int MOVE_FAILED_DOESNT_EXIST = -2;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
-     * if the specified package cannot be moved since its a system package.
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved since its a system package.
+     *
      * @hide
      */
     public static final int MOVE_FAILED_SYSTEM_PACKAGE = -3;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
-     * if the specified package cannot be moved since its forward locked.
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved since its forward locked.
+     *
      * @hide
      */
     public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
-     * if the specified package cannot be moved to the specified location.
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved to the specified location.
+     *
      * @hide
      */
     public static final int MOVE_FAILED_INVALID_LOCATION = -5;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
-     * if the specified package cannot be moved to the specified location.
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package cannot be moved to the specified location.
+     *
      * @hide
      */
     public static final int MOVE_FAILED_INTERNAL_ERROR = -6;
 
     /**
-     * Error code that is passed to the {@link IPackageMoveObserver} by
-     * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} if the
-     * specified package already has an operation pending in the
-     * {@link PackageHandler} queue.
+     * Error code that is passed to the {@link IPackageMoveObserver} if the
+     * specified package already has an operation pending in the queue.
      *
      * @hide
      */
@@ -1112,28 +1299,31 @@
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
 
     /**
-     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
-     * to indicate that the User will always be prompted the Intent Disambiguation Dialog if there
-     * are two or more Intent resolved for the IntentFilter's domain(s).
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that the User
+     * will always be prompted the Intent Disambiguation Dialog if there are two
+     * or more Intent resolved for the IntentFilter's domain(s).
      *
      * @hide
      */
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
 
     /**
-     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
-     * to indicate that the User will never be prompted the Intent Disambiguation Dialog if there
-     * are two or more resolution of the Intent. The default App for the domain(s) specified in the
-     * IntentFilter will also ALWAYS be used.
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that the User
+     * will never be prompted the Intent Disambiguation Dialog if there are two
+     * or more resolution of the Intent. The default App for the domain(s)
+     * specified in the IntentFilter will also ALWAYS be used.
      *
      * @hide
      */
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
 
     /**
-     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
-     * to indicate that the User may be prompted the Intent Disambiguation Dialog if there
-     * are two or more Intent resolved. The default App for the domain(s) specified in the
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that the User
+     * may be prompted the Intent Disambiguation Dialog if there are two or more
+     * Intent resolved. The default App for the domain(s) specified in the
      * IntentFilter will also NEVER be presented to the User.
      *
      * @hide
@@ -1141,12 +1331,13 @@
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
 
     /**
-     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
-     * to indicate that this app should always be considered as an ambiguous candidate for
-     * handling the matching Intent even if there are other candidate apps in the "always"
-     * state.  Put another way: if there are any 'always ask' apps in a set of more than
-     * one candidate app, then a disambiguation is *always* presented even if there is
-     * another candidate app with the 'always' state.
+     * Used as the {@code status} argument for
+     * {@link #updateIntentVerificationStatusAsUser} to indicate that this app
+     * should always be considered as an ambiguous candidate for handling the
+     * matching Intent even if there are other candidate apps in the "always"
+     * state. Put another way: if there are any 'always ask' apps in a set of
+     * more than one candidate app, then a disambiguation is *always* presented
+     * even if there is another candidate app with the 'always' state.
      *
      * @hide
      */
@@ -1711,6 +1902,16 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Wi-Fi Aware (NAN)
+     * networking.
+     *
+     * @hide PROPOSED_NAN_API
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_WIFI_NAN = "android.hardware.wifi.nan";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: This is a device dedicated to showing UI
      * on a vehicle headunit. A headunit here is defined to be inside a
      * vehicle that may or may not be moving. A headunit uses either a
@@ -1819,7 +2020,6 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: This device supports ethernet.
-     * @hide
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
@@ -1930,9 +2130,9 @@
             = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
 
     /**
-     * Extra field name for the ID of a intent filter pending verification. Passed to
-     * an intent filter verifier and is used to call back to
-     * {@link PackageManager#verifyIntentFilter(int, int)}
+     * Extra field name for the ID of a intent filter pending verification.
+     * Passed to an intent filter verifier and is used to call back to
+     * {@link #verifyIntentFilter}
      *
      * @hide
      */
@@ -2100,79 +2300,99 @@
     /**
      * Retrieve overall information about an application package that is
      * installed on the system.
-     * <p>
-     * Throws {@link NameNotFoundException} if a package with the given name can
-     * not be found on the system.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
-     *            desired package.
+     *         desired package.
      * @param flags Additional option flags. Use any combination of
-     *            {@link #GET_ACTIVITIES}, {@link #GET_GIDS},
-     *            {@link #GET_CONFIGURATIONS}, {@link #GET_INSTRUMENTATION},
-     *            {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
-     *            {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
-     *            {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
-     *            modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
+     *
      * @return A PackageInfo object containing information about the
-     *         package. If flag GET_UNINSTALLED_PACKAGES is set and if the
+     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the
      *         package is not found in the list of installed applications, the
      *         package information is retrieved from the list of uninstalled
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DONT_DELETE_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
-    public abstract PackageInfo getPackageInfo(String packageName, int flags)
+    public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags)
             throws NameNotFoundException;
 
     /**
      * @hide
      * Retrieve overall information about an application package that is
      * installed on the system.
-     * <p>
-     * Throws {@link NameNotFoundException} if a package with the given name can
-     * not be found on the system.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
-     *            desired package.
+     *         desired package.
      * @param flags Additional option flags. Use any combination of
-     *            {@link #GET_ACTIVITIES}, {@link #GET_GIDS},
-     *            {@link #GET_CONFIGURATIONS}, {@link #GET_INSTRUMENTATION},
-     *            {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
-     *            {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
-     *            {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to
-     *            modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      * @param userId The user id.
+     *
      * @return A PackageInfo object containing information about the
-     *         package. If flag GET_UNINSTALLED_PACKAGES is set and if the
+     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the
      *         package is not found in the list of installed applications, the
      *         package information is retrieved from the list of uninstalled
      *         applications (which includes installed applications as well as
      *         applications with data directory i.e. applications which had been
      *         deleted with {@code DONT_DELETE_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
-    public abstract PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
-            throws NameNotFoundException;
+    public abstract PackageInfo getPackageInfoAsUser(String packageName,
+            @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
 
     /**
      * Map from the current package names in use on the device to whatever
@@ -2214,9 +2434,6 @@
      * through packages. The current implementation will look for a main
      * activity in the category {@link Intent#CATEGORY_LEANBACK_LAUNCHER}, or
      * return null if no main leanback activities are found.
-     * <p>
-     * Throws {@link NameNotFoundException} if a package with the given name
-     * cannot be found on the system.
      *
      * @param packageName The name of the package to inspect.
      * @return Returns either a fully-qualified Intent that can be used to launch
@@ -2228,325 +2445,456 @@
     /**
      * Return an array of all of the secondary group-ids that have been assigned
      * to a package.
-     * <p>
-     * Throws {@link NameNotFoundException} if a package with the given name
-     * cannot be found on the system.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
-     *            desired package.
+     *         desired package.
      * @return Returns an int array of the assigned gids, or null if there are
      *         none.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      */
     public abstract int[] getPackageGids(String packageName)
             throws NameNotFoundException;
 
     /**
-     * @hide Return the uid associated with the given package name for the
-     * given user.
-     *
-     * <p>Throws {@link NameNotFoundException} if a package with the given
-     * name can not be found on the system.
+     * Return an array of all of the secondary group-ids that have been assigned
+     * to a package.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
-     *                    desired package.
-     * @param userHandle The user handle identifier to look up the package under.
-     *
-     * @return Returns an integer uid who owns the given package name.
+     *            desired package.
+     * @return Returns an int array of the assigned gids, or null if there are
+     *         none.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      */
-    public abstract int getPackageUid(String packageName, int userHandle)
+    public abstract int[] getPackageGids(String packageName, @PackageInfoFlags int flags)
             throws NameNotFoundException;
 
     /**
+     * Return the UID associated with the given package name.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @return Returns an integer UID who owns the given package name.
+     * @throws NameNotFoundException if a package with the given name can not be
+     *             found on the system.
+     */
+    public abstract int getPackageUid(String packageName, @PackageInfoFlags int flags)
+            throws NameNotFoundException;
+
+    /**
+     * Return the UID associated with the given package name.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @param userId The user handle identifier to look up the package under.
+     * @return Returns an integer UID who owns the given package name.
+     * @throws NameNotFoundException if a package with the given name can not be
+     *             found on the system.
+     * @hide
+     */
+    public abstract int getPackageUidAsUser(String packageName, @UserIdInt int userId)
+            throws NameNotFoundException;
+
+    /**
+     * Return the UID associated with the given package name.
+     *
+     * @param packageName The full name (i.e. com.google.apps.contacts) of the
+     *            desired package.
+     * @param userId The user handle identifier to look up the package under.
+     * @return Returns an integer UID who owns the given package name.
+     * @throws NameNotFoundException if a package with the given name can not be
+     *             found on the system.
+     * @hide
+     */
+    public abstract int getPackageUidAsUser(String packageName, @PackageInfoFlags int flags,
+            @UserIdInt int userId) throws NameNotFoundException;
+
+    /**
      * Retrieve all of the information we know about a particular permission.
      *
-     * <p>Throws {@link NameNotFoundException} if a permission with the given
-     * name cannot be found on the system.
-     *
      * @param name The fully qualified name (i.e. com.google.permission.LOGIN)
-     *             of the permission you are interested in.
+     *         of the permission you are interested in.
      * @param flags Additional option flags.  Use {@link #GET_META_DATA} to
-     * retrieve any meta-data associated with the permission.
+     *         retrieve any meta-data associated with the permission.
      *
      * @return Returns a {@link PermissionInfo} containing information about the
      *         permission.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     *
+     * @see #GET_META_DATA
      */
-    public abstract PermissionInfo getPermissionInfo(String name, int flags)
+    public abstract PermissionInfo getPermissionInfo(String name, @PermissionInfoFlags int flags)
             throws NameNotFoundException;
 
     /**
      * Query for all of the permissions associated with a particular group.
      *
-     * <p>Throws {@link NameNotFoundException} if the given group does not
-     * exist.
-     *
      * @param group The fully qualified name (i.e. com.google.permission.LOGIN)
-     *             of the permission group you are interested in.  Use null to
-     *             find all of the permissions not associated with a group.
+     *         of the permission group you are interested in.  Use null to
+     *         find all of the permissions not associated with a group.
      * @param flags Additional option flags.  Use {@link #GET_META_DATA} to
-     * retrieve any meta-data associated with the permissions.
+     *         retrieve any meta-data associated with the permissions.
      *
      * @return Returns a list of {@link PermissionInfo} containing information
-     * about all of the permissions in the given group.
+     *             about all of the permissions in the given group.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     *
+     * @see #GET_META_DATA
      */
     public abstract List<PermissionInfo> queryPermissionsByGroup(String group,
-            int flags) throws NameNotFoundException;
+            @PermissionInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Retrieve all of the information we know about a particular group of
      * permissions.
      *
-     * <p>Throws {@link NameNotFoundException} if a permission group with the given
-     * name cannot be found on the system.
-     *
      * @param name The fully qualified name (i.e. com.google.permission_group.APPS)
-     *             of the permission you are interested in.
+     *         of the permission you are interested in.
      * @param flags Additional option flags.  Use {@link #GET_META_DATA} to
-     * retrieve any meta-data associated with the permission group.
+     *         retrieve any meta-data associated with the permission group.
      *
      * @return Returns a {@link PermissionGroupInfo} containing information
-     * about the permission.
+     *         about the permission.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     *
+     * @see #GET_META_DATA
      */
     public abstract PermissionGroupInfo getPermissionGroupInfo(String name,
-            int flags) throws NameNotFoundException;
+            @PermissionGroupInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Retrieve all of the known permission groups in the system.
      *
      * @param flags Additional option flags.  Use {@link #GET_META_DATA} to
-     * retrieve any meta-data associated with the permission group.
+     *         retrieve any meta-data associated with the permission group.
      *
      * @return Returns a list of {@link PermissionGroupInfo} containing
-     * information about all of the known permission groups.
+     *         information about all of the known permission groups.
+     *
+     * @see #GET_META_DATA
      */
-    public abstract List<PermissionGroupInfo> getAllPermissionGroups(int flags);
+    public abstract List<PermissionGroupInfo> getAllPermissionGroups(
+            @PermissionGroupInfoFlags int flags);
 
     /**
      * Retrieve all of the information we know about a particular
      * package/application.
      *
-     * <p>Throws {@link NameNotFoundException} if an application with the given
-     * package name cannot be found on the system.
-     *
      * @param packageName The full name (i.e. com.google.apps.contacts) of an
-     *                    application.
+     *         application.
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
-     * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return  {@link ApplicationInfo} Returns ApplicationInfo object containing
-     *         information about the package.
-     *         If flag GET_UNINSTALLED_PACKAGES is set and  if the package is not
-     *         found in the list of installed applications,
-     *         the application information is retrieved from the
-     *         list of uninstalled applications(which includes
-     *         installed applications as well as applications
-     *         with data directory ie applications which had been
+     * @return An {@link ApplicationInfo} containing information about the
+     *         package. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set and if the
+     *         package is not found in the list of installed applications, the
+     *         application information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
      *         deleted with {@code DONT_DELETE_DATA} flag set).
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract ApplicationInfo getApplicationInfo(String packageName,
-            int flags) throws NameNotFoundException;
+            @ApplicationInfoFlags int flags) throws NameNotFoundException;
+
+    /** {@hide} */
+    public abstract ApplicationInfo getApplicationInfoAsUser(String packageName,
+            @ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException;
 
     /**
      * Retrieve all of the information we know about a particular activity
      * class.
      *
-     * <p>Throws {@link NameNotFoundException} if an activity with the given
-     * class name cannot be found on the system.
-     *
      * @param component The full component name (i.e.
      * com.google.apps.contacts/com.google.apps.contacts.ContactsList) of an Activity
      * class.
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
-     * to modify the data (in ApplicationInfo) returned.
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return {@link ActivityInfo} containing information about the activity.
+     * @return An {@link ActivityInfo} containing information about the activity.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      *
-     * @see #GET_INTENT_FILTERS
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract ActivityInfo getActivityInfo(ComponentName component,
-            int flags) throws NameNotFoundException;
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Retrieve all of the information we know about a particular receiver
      * class.
      *
-     * <p>Throws {@link NameNotFoundException} if a receiver with the given
-     * class name cannot be found on the system.
-     *
      * @param component The full component name (i.e.
      * com.google.apps.calendar/com.google.apps.calendar.CalendarAlarm) of a Receiver
      * class.
-     * @param flags Additional option flags.  Use any combination of
-     * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
-     * to modify the data returned.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return {@link ActivityInfo} containing information about the receiver.
+     * @return An {@link ActivityInfo} containing information about the receiver.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      *
-     * @see #GET_INTENT_FILTERS
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract ActivityInfo getReceiverInfo(ComponentName component,
-            int flags) throws NameNotFoundException;
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Retrieve all of the information we know about a particular service
      * class.
      *
-     * <p>Throws {@link NameNotFoundException} if a service with the given
-     * class name cannot be found on the system.
-     *
      * @param component The full component name (i.e.
      * com.google.apps.media/com.google.apps.media.BackgroundPlayback) of a Service
      * class.
-     * @param flags Additional option flags.  Use any combination of
-     * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
-     * to modify the data returned.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return ServiceInfo containing information about the service.
+     * @return A {@link ServiceInfo} object containing information about the service.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract ServiceInfo getServiceInfo(ComponentName component,
-            int flags) throws NameNotFoundException;
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Retrieve all of the information we know about a particular content
      * provider class.
      *
-     * <p>Throws {@link NameNotFoundException} if a provider with the given
-     * class name cannot be found on the system.
-     *
      * @param component The full component name (i.e.
      * com.google.providers.media/com.google.providers.media.MediaProvider) of a
      * ContentProvider class.
-     * @param flags Additional option flags.  Use any combination of
-     * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
-     * to modify the data returned.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return ProviderInfo containing information about the service.
+     * @return A {@link ProviderInfo} object containing information about the provider.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract ProviderInfo getProviderInfo(ComponentName component,
-            int flags) throws NameNotFoundException;
+            @ComponentInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Return a List of all packages that are installed
      * on the device.
      *
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_ACTIVITIES},
-     * {@link #GET_GIDS},
-     * {@link #GET_CONFIGURATIONS},
-     * {@link #GET_INSTRUMENTATION},
-     * {@link #GET_PERMISSIONS},
-     * {@link #GET_PROVIDERS},
-     * {@link #GET_RECEIVERS},
-     * {@link #GET_SERVICES},
-     * {@link #GET_SIGNATURES},
-     * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return A List of PackageInfo objects, one for each package that is
-     *         installed on the device.  In the unlikely case of there being no
-     *         installed packages, an empty list is returned.
-     *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
-     *         applications including those deleted with {@code DONT_DELETE_DATA}
-     *         (partially installed apps with data directory) will be returned.
+     * @return A List of PackageInfo objects, one for each installed package,
+     *         containing information about the package.  In the unlikely case
+     *         there are no installed packages, an empty list is returned. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DONT_DELETE_DATA} flag set).
      *
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
-    public abstract List<PackageInfo> getInstalledPackages(int flags);
+    public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
 
     /**
      * Return a List of all installed packages that are currently
      * holding any of the given permissions.
      *
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_ACTIVITIES},
-     * {@link #GET_GIDS},
-     * {@link #GET_CONFIGURATIONS},
-     * {@link #GET_INSTRUMENTATION},
-     * {@link #GET_PERMISSIONS},
-     * {@link #GET_PROVIDERS},
-     * {@link #GET_RECEIVERS},
-     * {@link #GET_SERVICES},
-     * {@link #GET_SIGNATURES},
-     * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return Returns a List of PackageInfo objects, one for each installed
-     * application that is holding any of the permissions that were provided.
+     * @return A List of PackageInfo objects, one for each installed package
+     *         that holds any of the permissions that were provided, containing
+     *         information about the package. If no installed packages hold any
+     *         of the permissions, an empty list is returned. If flag
+     *         {@code MATCH_UNINSTALLED_PACKAGES} is set, the package information
+     *         is retrieved from the list of uninstalled applications (which
+     *         includes installed applications as well as applications with data
+     *         directory i.e. applications which had been deleted with
+     *         {@code DONT_DELETE_DATA} flag set).
      *
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract List<PackageInfo> getPackagesHoldingPermissions(
-            String[] permissions, int flags);
+            String[] permissions, @PackageInfoFlags int flags);
 
     /**
      * Return a List of all packages that are installed on the device, for a specific user.
      * Requesting a list of installed packages for another user
      * will require the permission INTERACT_ACROSS_USERS_FULL.
+     *
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_ACTIVITIES},
-     * {@link #GET_GIDS},
-     * {@link #GET_CONFIGURATIONS},
-     * {@link #GET_INSTRUMENTATION},
-     * {@link #GET_PERMISSIONS},
-     * {@link #GET_PROVIDERS},
-     * {@link #GET_RECEIVERS},
-     * {@link #GET_SERVICES},
-     * {@link #GET_SIGNATURES},
-     * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      * @param userId The user for whom the installed packages are to be listed
      *
-     * @return A List of PackageInfo objects, one for each package that is
-     *         installed on the device.  In the unlikely case of there being no
-     *         installed packages, an empty list is returned.
-     *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
-     *         applications including those deleted with {@code DONT_DELETE_DATA}
-     *         (partially installed apps with data directory) will be returned.
+     * @return A List of PackageInfo objects, one for each installed package,
+     *         containing information about the package.  In the unlikely case
+     *         there are no installed packages, an empty list is returned. If
+     *         flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the package
+     *         information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DONT_DELETE_DATA} flag set).
      *
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      *
      * @hide
      */
-    public abstract List<PackageInfo> getInstalledPackages(int flags, int userId);
+    public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags,
+            @UserIdInt int userId);
 
     /**
      * Check whether a particular package has been granted a particular
@@ -2835,7 +3183,7 @@
      * @return Returns an array of one or more packages assigned to the user
      * id, or null if there are no known packages with the given id.
      */
-    public abstract String[] getPackagesForUid(int uid);
+    public abstract @Nullable String[] getPackagesForUid(int uid);
 
     /**
      * Retrieve the official name associated with a user id.  This name is
@@ -2848,7 +3196,7 @@
      * @return Returns a unique name for the given user id, or null if the
      * user id is not currently assigned.
      */
-    public abstract String getNameForUid(int uid);
+    public abstract @Nullable String getNameForUid(int uid);
 
     /**
      * Return the user id associated with a shared user name. Multiple
@@ -2858,8 +3206,9 @@
      * shared user.
      *
      * @param sharedUserName The shared user name whose uid is to be retrieved.
-     * @return Returns the uid associated with the shared user, or  NameNotFoundException
-     * if the shared user name is not being used by any installed packages
+     * @return Returns the UID associated with the shared user.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
      * @hide
      */
     public abstract int getUidForSharedUser(String sharedUserName)
@@ -2873,20 +3222,23 @@
      *
      * @param flags Additional option flags. Use any combination of
      * {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
-     * {@link #GET_UNINSTALLED_PACKAGES} to modify the data returned.
+     * {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     * to modify the data returned.
      *
-     * @return Returns a List of ApplicationInfo objects, one for each application that
-     *         is installed on the device.  In the unlikely case of there being
-     *         no installed applications, an empty list is returned.
-     *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
-     *         applications including those deleted with {@code DONT_DELETE_DATA}
-     *         (partially installed apps with data directory) will be returned.
+     * @return A List of ApplicationInfo objects, one for each installed application.
+     *         In the unlikely case there are no installed packages, an empty list
+     *         is returned. If flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the
+     *         application information is retrieved from the list of uninstalled
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DONT_DELETE_DATA} flag set).
      *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
-     * @see #GET_UNINSTALLED_PACKAGES
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
-    public abstract List<ApplicationInfo> getInstalledApplications(int flags);
+    public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags);
 
     /**
      * Gets the ephemeral applications the user recently used. Requires
@@ -2916,6 +3268,8 @@
      * @see #setEphemeralCookie(byte[])
      * @see #getEphemeralCookie()
      * @see #getEphemeralCookieMaxSizeBytes()
+     *
+     * @hide
      */
     public abstract boolean isEphemeralApplication();
 
@@ -2928,6 +3282,8 @@
      * @see #isEphemeralApplication()
      * @see #setEphemeralCookie(byte[])
      * @see #getEphemeralCookie()
+     *
+     * @hide
      */
     public abstract int getEphemeralCookieMaxSizeBytes();
 
@@ -2944,6 +3300,8 @@
      * @see #isEphemeralApplication()
      * @see #setEphemeralCookie(byte[])
      * @see #getEphemeralCookieMaxSizeBytes()
+     *
+     * @hide
      */
     public abstract @NonNull byte[] getEphemeralCookie();
 
@@ -2961,7 +3319,9 @@
      *
      * @see #isEphemeralApplication()
      * @see #getEphemeralCookieMaxSizeBytes()
-     * @see #getEphemeralCookie();
+     * @see #getEphemeralCookie()
+     *
+     * @hide
      */
     public abstract boolean setEphemeralCookie(@NonNull  byte[] cookie);
 
@@ -3007,21 +3367,37 @@
      *
      * @param intent An intent containing all of the desired specification
      *               (action, data, type, category, and/or component).
-     * @param flags Additional option flags.  The most important is
-     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
-     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned. The most important is
+     *         {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     *         those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
      *
-     * @return Returns a ResolveInfo containing the final activity intent that
-     *         was determined to be the best action.  Returns null if no
+     * @return Returns a ResolveInfo object containing the final activity intent
+     *         that was determined to be the best action.  Returns null if no
      *         matching activity was found. If multiple matching activities are
-     *         found and there is no default set, returns a ResolveInfo
+     *         found and there is no default set, returns a ResolveInfo object
      *         containing something else, such as the activity resolver.
      *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
-    public abstract ResolveInfo resolveActivity(Intent intent, int flags);
+    public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags);
 
     /**
      * Determine the best action to perform for a given Intent for a given user. This
@@ -3037,72 +3413,117 @@
      *
      * @param intent An intent containing all of the desired specification
      *               (action, data, type, category, and/or component).
-     * @param flags Additional option flags.  The most important is
-     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
-     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned. The most important is
+     *         {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     *         those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
      * @param userId The user id.
      *
-     * @return Returns a ResolveInfo containing the final activity intent that
-     *         was determined to be the best action.  Returns null if no
+     * @return Returns a ResolveInfo object containing the final activity intent
+     *         that was determined to be the best action.  Returns null if no
      *         matching activity was found. If multiple matching activities are
-     *         found and there is no default set, returns a ResolveInfo
+     *         found and there is no default set, returns a ResolveInfo object
      *         containing something else, such as the activity resolver.
      *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      *
      * @hide
      */
-    public abstract ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
+    public abstract ResolveInfo resolveActivityAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId);
 
     /**
      * Retrieve all activities that can be performed for the given intent.
      *
      * @param intent The desired intent as per resolveActivity().
-     * @param flags Additional option flags.  The most important is
-     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
-     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned. The most important is
+     *         {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     *         those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     *         Or, set {@link #MATCH_ALL} to prevent any filtering of the results.
      *
-     * You can also set {@link #MATCH_ALL} for preventing the filtering of the results.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching activity, ordered from best to worst. In other words, the
+     *         first item is what would be returned by {@link #resolveActivity}.
+     *         If there are no matching activities, an empty list is returned.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         Activity. These are ordered from best to worst match -- that
-     *         is, the first item in the list is what is returned by
-     *         {@link #resolveActivity}.  If there are no matching activities, an empty
-     *         list is returned.
-     *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
-            int flags);
+            @ResolveInfoFlags int flags);
 
     /**
      * Retrieve all activities that can be performed for the given intent, for a specific user.
      *
      * @param intent The desired intent as per resolveActivity().
-     * @param flags Additional option flags.  The most important is
-     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
-     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned. The most important is
+     *         {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     *         those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     *         Or, set {@link #MATCH_ALL} to prevent any filtering of the results.
      *
-     * You can also set {@link #MATCH_ALL} for preventing the filtering of the results.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching activity, ordered from best to worst. In other words, the
+     *         first item is what would be returned by {@link #resolveActivity}.
+     *         If there are no matching activities, an empty list is returned.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         Activity. These are ordered from best to worst match -- that
-     *         is, the first item in the list is what is returned by
-     *         {@link #resolveActivity}.  If there are no matching activities, an empty
-     *         list is returned.
-     *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     *
      * @hide
      */
     public abstract List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
-            int flags, int userId);
-
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
 
     /**
      * Retrieve a set of activities that should be presented to the user as
@@ -3118,158 +3539,359 @@
      * @param specifics An array of Intents that should be resolved to the
      *                  first specific results.  Can be null.
      * @param intent The desired intent as per resolveActivity().
-     * @param flags Additional option flags.  The most important is
-     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
-     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned. The most important is
+     *         {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     *         those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         Activity. These are ordered first by all of the intents resolved
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching activity. The list is ordered first by all of the intents resolved
      *         in <var>specifics</var> and then any additional activities that
      *         can handle <var>intent</var> but did not get included by one of
      *         the <var>specifics</var> intents.  If there are no matching
      *         activities, an empty list is returned.
      *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract List<ResolveInfo> queryIntentActivityOptions(
-            ComponentName caller, Intent[] specifics, Intent intent, int flags);
+            ComponentName caller, Intent[] specifics, Intent intent, @ResolveInfoFlags int flags);
 
     /**
      * Retrieve all receivers that can handle a broadcast of the given intent.
      *
      * @param intent The desired intent as per resolveActivity().
-     * @param flags Additional option flags.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         Receiver. These are ordered from first to last in priority.  If
-     *         there are no matching receivers, an empty list is returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching receiver, ordered from best to worst. If there are no matching
+     *         receivers, an empty list or null is returned.
      *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent,
-            int flags);
+            @ResolveInfoFlags int flags);
 
     /**
      * Retrieve all receivers that can handle a broadcast of the given intent, for a specific
      * user.
      *
      * @param intent The desired intent as per resolveActivity().
-     * @param flags Additional option flags.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      * @param userId The userId of the user being queried.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         Receiver. These are ordered from first to last in priority.  If
-     *         there are no matching receivers, an empty list or {@code null} is returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching receiver, ordered from best to worst. If there are no matching
+     *         receivers, an empty list or null is returned.
      *
-     * @see #MATCH_DEFAULT_ONLY
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     *
      * @hide
      */
-    public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent,
-            int flags, int userId);
+    public abstract List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /** {@hide} */
+    @Deprecated
+    public List<ResolveInfo> queryBroadcastReceivers(Intent intent,
+            @ResolveInfoFlags int flags, @UserIdInt int userId) {
+        Log.w(TAG, "STAHP USING HIDDEN APIS KTHX");
+        return queryBroadcastReceiversAsUser(intent, flags, userId);
+    }
 
     /**
      * Determine the best service to handle for a given Intent.
      *
      * @param intent An intent containing all of the desired specification
      *               (action, data, type, category, and/or component).
-     * @param flags Additional option flags.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return Returns a ResolveInfo containing the final service intent that
-     *         was determined to be the best action.  Returns null if no
+     * @return Returns a ResolveInfo object containing the final service intent
+     *         that was determined to be the best action.  Returns null if no
      *         matching service was found.
      *
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
-    public abstract ResolveInfo resolveService(Intent intent, int flags);
+    public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
 
     /**
      * Retrieve all services that can match the given intent.
      *
      * @param intent The desired intent as per resolveService().
-     * @param flags Additional option flags.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         ServiceInfo. These are ordered from best to worst match -- that
-     *         is, the first item in the list is what is returned by
-     *         resolveService().  If there are no matching services, an empty
-     *         list or {@code null} is returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching service, ordered from best to worst. In other words, the first
+     *         item is what would be returned by {@link #resolveService}. If there are
+     *         no matching services, an empty list or null is returned.
      *
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract List<ResolveInfo> queryIntentServices(Intent intent,
-            int flags);
+            @ResolveInfoFlags int flags);
 
     /**
      * Retrieve all services that can match the given intent for a given user.
      *
      * @param intent The desired intent as per resolveService().
-     * @param flags Additional option flags.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      * @param userId The user id.
      *
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         ServiceInfo. These are ordered from best to worst match -- that
-     *         is, the first item in the list is what is returned by
-     *         resolveService().  If there are no matching services, an empty
-     *         list or {@code null} is returned.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching service, ordered from best to worst. In other words, the first
+     *         item is what would be returned by {@link #resolveService}. If there are
+     *         no matching services, an empty list or null is returned.
      *
-     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      *
      * @hide
      */
     public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent,
-            int flags, int userId);
-
-    /** {@hide} */
-    public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
-            Intent intent, int flags, int userId);
+            @ResolveInfoFlags int flags, @UserIdInt int userId);
 
     /**
      * Retrieve all providers that can match the given intent.
      *
      * @param intent An intent containing all of the desired specification
      *            (action, data, type, category, and/or component).
-     * @param flags Additional option flags.
-     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
-     *         ProviderInfo. These are ordered from best to worst match. If
-     *         there are no matching providers, an empty list or {@code null} is returned.
-     * @see #GET_INTENT_FILTERS
-     * @see #GET_RESOLVED_FILTER
-     */
-    public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags);
-
-    /**
-     * Find a single content provider by its base path name.
-     *
-     * @param name The name of the provider to find.
-     * @param flags Additional option flags.  Currently should always be 0.
-     *
-     * @return ContentProviderInfo Information about the provider, if found,
-     *         else null.
-     */
-    public abstract ProviderInfo resolveContentProvider(String name,
-            int flags);
-
-    /**
-     * Find a single content provider by its base path name.
-     *
-     * @param name The name of the provider to find.
-     * @param flags Additional option flags.  Currently should always be 0.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      * @param userId The user id.
      *
-     * @return ContentProviderInfo Information about the provider, if found,
-     *         else null.
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching provider, ordered from best to worst. If there are no
+     *         matching services, an empty list or null is returned.
+     *
+     * @see #GET_META_DATA
+     * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     *
      * @hide
      */
-    public abstract ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId);
+    public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
+            Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId);
+
+    /**
+     * Retrieve all providers that can match the given intent.
+     *
+     * @param intent An intent containing all of the desired specification
+     *            (action, data, type, category, and/or component).
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_RESOLVED_FILTER},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #MATCH_ALL},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_DEFAULT_ONLY}, {@link #MATCH_ENCRYPTION_AWARE},
+     *         {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE}, {@link #MATCH_ENCRYPTION_UNAWARE},
+     *         {@link #MATCH_SYSTEM_ONLY}, {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
+     *
+     * @return Returns a List of ResolveInfo objects containing one entry for each
+     *         matching provider, ordered from best to worst. If there are no
+     *         matching services, an empty list or null is returned.
+     *
+     * @see #GET_META_DATA
+     * @see #GET_RESOLVED_FILTER
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     */
+    public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent,
+            @ResolveInfoFlags int flags);
+
+    /**
+     * Find a single content provider by its base path name.
+     *
+     * @param name The name of the provider to find.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
+     *
+     * @return A {@link ProviderInfo} object containing information about the provider.
+     *         If a provider was not found, returns null.
+     *
+     * @see #GET_META_DATA
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     */
+    public abstract ProviderInfo resolveContentProvider(String name,
+            @ComponentInfoFlags int flags);
+
+    /**
+     * Find a single content provider by its base path name.
+     *
+     * @param name The name of the provider to find.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
+     * @param userId The user id.
+     *
+     * @return A {@link ProviderInfo} object containing information about the provider.
+     *         If a provider was not found, returns null.
+     *
+     * @see #GET_META_DATA
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
+     *
+     * @hide
+     */
+    public abstract ProviderInfo resolveContentProviderAsUser(String name,
+            @ComponentInfoFlags int flags, @UserIdInt int userId);
 
     /**
      * Retrieve content provider information.
@@ -3282,33 +3904,56 @@
      *                    all content providers are returned.
      * @param uid If <var>processName</var> is non-null, this is the required
      *        uid owning the requested content providers.
-     * @param flags Additional option flags.  Currently should always be 0.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}, {@link #GET_SHARED_LIBRARY_FILES},
+     *         {@link #MATCH_ALL}, {@link #MATCH_DEFAULT_ONLY},
+     *         {@link #MATCH_DISABLED_COMPONENTS},  {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_ENCRYPTION_AWARE}, {@link #MATCH_ENCRYPTION_AWARE_AND_UNAWARE},
+     *         {@link #MATCH_ENCRYPTION_UNAWARE}, {@link #MATCH_SYSTEM_ONLY}
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return A List&lt;ContentProviderInfo&gt; containing one entry for each
-     *         content provider either patching <var>processName</var> or, if
+     * @return A list of {@link ProviderInfo} objects containing one entry for
+     *         each provider either matching <var>processName</var> or, if
      *         <var>processName</var> is null, all known content providers.
      *         <em>If there are no matching providers, null is returned.</em>
+     *
+     * @see #GET_META_DATA
+     * @see #GET_SHARED_LIBRARY_FILES
+     * @see #MATCH_ALL
+     * @see #MATCH_DEBUG_TRIAGED_MISSING
+     * @see #MATCH_DEFAULT_ONLY
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_ENCRYPTION_AWARE
+     * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+     * @see #MATCH_ENCRYPTION_UNAWARE
+     * @see #MATCH_SYSTEM_ONLY
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
     public abstract List<ProviderInfo> queryContentProviders(
-            String processName, int uid, int flags);
+            String processName, int uid, @ComponentInfoFlags int flags);
 
     /**
      * Retrieve all of the information we know about a particular
      * instrumentation class.
      *
-     * <p>Throws {@link NameNotFoundException} if instrumentation with the
-     * given class name cannot be found on the system.
-     *
      * @param className The full name (i.e.
      *                  com.google.apps.contacts.InstrumentList) of an
      *                  Instrumentation class.
-     * @param flags Additional option flags.  Currently should always be 0.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}
+     *         to modify the data returned.
      *
-     * @return InstrumentationInfo containing information about the
+     * @return An {@link InstrumentationInfo} object containing information about the
      *         instrumentation.
+     * @throws NameNotFoundException if a package with the given name cannot be
+     *             found on the system.
+     *
+     * @see #GET_META_DATA
      */
-    public abstract InstrumentationInfo getInstrumentationInfo(
-            ComponentName className, int flags) throws NameNotFoundException;
+    public abstract InstrumentationInfo getInstrumentationInfo(ComponentName className,
+            @InstrumentationInfoFlags int flags) throws NameNotFoundException;
 
     /**
      * Retrieve information about available instrumentation code.  May be used
@@ -3318,14 +3963,18 @@
      * @param targetPackage If null, all instrumentation is returned; only the
      *                      instrumentation targeting this package name is
      *                      returned.
-     * @param flags Additional option flags.  Currently should always be 0.
+     * @param flags Additional option flags. Use any combination of
+     *         {@link #GET_META_DATA}
+     *         to modify the data returned.
      *
-     * @return A List&lt;InstrumentationInfo&gt; containing one entry for each
-     *         matching available Instrumentation.  Returns an empty list if
-     *         there is no instrumentation available for the given package.
+     * @return A list of {@link InstrumentationInfo} objects containing one
+     *         entry for each matching instrumentation. If there are no
+     *         instrumentation available, returns and empty list. 
+     *
+     * @see #GET_META_DATA
      */
-    public abstract List<InstrumentationInfo> queryInstrumentation(
-            String targetPackage, int flags);
+    public abstract List<InstrumentationInfo> queryInstrumentation(String targetPackage,
+            @InstrumentationInfoFlags int flags);
 
     /**
      * Retrieve an image from a package.  This is a low-level API used by
@@ -3728,8 +4377,8 @@
             throws NameNotFoundException;
 
     /** @hide */
-    public abstract Resources getResourcesForApplicationAsUser(String appPackageName, int userId)
-            throws NameNotFoundException;
+    public abstract Resources getResourcesForApplicationAsUser(String appPackageName,
+            @UserIdInt int userId) throws NameNotFoundException;
 
     /**
      * Retrieve overall information about an application package defined
@@ -3737,38 +4386,55 @@
      *
      * @param archiveFilePath The path to the archive file
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_ACTIVITIES},
-     * {@link #GET_GIDS},
-     * {@link #GET_CONFIGURATIONS},
-     * {@link #GET_INSTRUMENTATION},
-     * {@link #GET_PERMISSIONS},
-     * {@link #GET_PROVIDERS},
-     * {@link #GET_RECEIVERS},
-     * {@link #GET_SERVICES},
-     * {@link #GET_SIGNATURES}, to modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return Returns the information about the package. Returns
-     * null if the package could not be successfully parsed.
+     * @return A PackageInfo object containing information about the
+     *         package archive. If the package could not be parsed,
+     *         returns null.
      *
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      *
      */
-    public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
+    public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) {
         final PackageParser parser = new PackageParser();
         final File apkFile = new File(archiveFilePath);
         try {
+            if ((flags & (MATCH_ENCRYPTION_UNAWARE | MATCH_ENCRYPTION_AWARE)) != 0) {
+                // Caller expressed an explicit opinion about what encryption
+                // aware/unaware components they want to see, so fall through and
+                // give them what they want
+            } else {
+                // Caller expressed no opinion, so match everything
+                flags |= MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
+            }
+
             PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
             if ((flags & GET_SIGNATURES) != 0) {
                 parser.collectCertificates(pkg, 0);
-                parser.collectManifestDigest(pkg);
             }
             PackageUserState state = new PackageUserState();
             return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
@@ -3778,217 +4444,64 @@
     }
 
     /**
-     * @hide Install a package. Since this may take a little while, the result
-     *       will be posted back to the given observer. An installation will
-     *       fail if the calling context lacks the
-     *       {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if
-     *       the package named in the package file's manifest is already
-     *       installed, or if there's no space available on the device.
-     * @param packageURI The location of the package file to install. This can
-     *            be a 'file:' or a 'content:' URI.
-     * @param observer An observer callback to get notified when the package
-     *            installation is complete.
-     *            {@link IPackageInstallObserver#packageInstalled(String, int)}
-     *            will be called when that happens. This parameter must not be
-     *            null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     *            {@link #INSTALL_REPLACE_EXISTING},
-     *            {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that
-     *            is performing the installation. This identifies which market
-     *            the package came from.
-     * @deprecated Use {@link #installPackage(Uri, PackageInstallObserver, int,
-     *             String)} instead. This method will continue to be supported
-     *             but the older observer interface will not get additional
-     *             failure details.
+     * @deprecated replaced by {@link PackageInstaller}
+     * @hide
      */
-    // @SystemApi
+    @Deprecated
     public abstract void installPackage(
-            Uri packageURI, IPackageInstallObserver observer, int flags,
+            Uri packageURI, IPackageInstallObserver observer, @InstallFlags int flags,
             String installerPackageName);
 
     /**
-     * Similar to
-     * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
-     * with an extra verification file provided.
-     *
-     * @param packageURI The location of the package file to install. This can
-     *            be a 'file:' or a 'content:' URI.
-     * @param observer An observer callback to get notified when the package
-     *            installation is complete.
-     *            {@link IPackageInstallObserver#packageInstalled(String, int)}
-     *            will be called when that happens. This parameter must not be
-     *            null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     *            {@link #INSTALL_REPLACE_EXISTING},
-     *            {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that
-     *            is performing the installation. This identifies which market
-     *            the package came from.
-     * @param verificationURI The location of the supplementary verification
-     *            file. This can be a 'file:' or a 'content:' URI. May be
-     *            {@code null}.
-     * @param manifestDigest an object that holds the digest of the package
-     *            which can be used to verify ownership. May be {@code null}.
-     * @param encryptionParams if the package to be installed is encrypted,
-     *            these parameters describing the encryption and authentication
-     *            used. May be {@code null}.
+     * @deprecated replaced by {@link PackageInstaller}
      * @hide
-     * @deprecated Use {@link #installPackageWithVerification(Uri,
-     *             PackageInstallObserver, int, String, Uri, ManifestDigest,
-     *             ContainerEncryptionParams)} instead. This method will
-     *             continue to be supported but the older observer interface
-     *             will not get additional failure details.
      */
-    // @SystemApi
+    @Deprecated
     public abstract void installPackageWithVerification(Uri packageURI,
-            IPackageInstallObserver observer, int flags, String installerPackageName,
-            Uri verificationURI, ManifestDigest manifestDigest,
-            ContainerEncryptionParams encryptionParams);
+            IPackageInstallObserver observer, @InstallFlags int flags, String installerPackageName,
+            Uri verificationURI, ContainerEncryptionParams encryptionParams);
 
     /**
-     * Similar to
-     * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
-     * with an extra verification information provided.
-     *
-     * @param packageURI The location of the package file to install. This can
-     *            be a 'file:' or a 'content:' URI.
-     * @param observer An observer callback to get notified when the package
-     *            installation is complete.
-     *            {@link IPackageInstallObserver#packageInstalled(String, int)}
-     *            will be called when that happens. This parameter must not be
-     *            null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     *            {@link #INSTALL_REPLACE_EXISTING},
-     *            {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that
-     *            is performing the installation. This identifies which market
-     *            the package came from.
-     * @param verificationParams an object that holds signal information to
-     *            assist verification. May be {@code null}.
-     * @param encryptionParams if the package to be installed is encrypted,
-     *            these parameters describing the encryption and authentication
-     *            used. May be {@code null}.
+     * @deprecated replaced by {@link PackageInstaller}
      * @hide
-     * @deprecated Use {@link #installPackageWithVerificationAndEncryption(Uri,
-     *             PackageInstallObserver, int, String, VerificationParams,
-     *             ContainerEncryptionParams)} instead. This method will
-     *             continue to be supported but the older observer interface
-     *             will not get additional failure details.
      */
     @Deprecated
     public abstract void installPackageWithVerificationAndEncryption(Uri packageURI,
-            IPackageInstallObserver observer, int flags, String installerPackageName,
-            VerificationParams verificationParams,
-            ContainerEncryptionParams encryptionParams);
-
-    // Package-install variants that take the new, expanded form of observer interface.
-    // Note that these *also* take the original observer type and will redundantly
-    // report the same information to that observer if supplied; but it is not required.
+            IPackageInstallObserver observer, @InstallFlags int flags, String installerPackageName,
+            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams);
 
     /**
+     * @deprecated replaced by {@link PackageInstaller}
      * @hide
-     *
-     * Install a package. Since this may take a little while, the result will
-     * be posted back to the given observer.  An installation will fail if the calling context
-     * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
-     * package named in the package file's manifest is already installed, or if there's no space
-     * available on the device.
-     *
-     * @param packageURI The location of the package file to install.  This can be a 'file:' or a
-     * 'content:' URI.
-     * @param observer An observer callback to get notified when the package installation is
-     * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
-     * called when that happens. This parameter must not be null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that is performing the
-     * installation. This identifies which market the package came from.
      */
-    public abstract void installPackage(
-            Uri packageURI, PackageInstallObserver observer,
-            int flags, String installerPackageName);
-
+    @Deprecated
+    public abstract void installPackage(Uri packageURI, PackageInstallObserver observer,
+            @InstallFlags int flags, String installerPackageName);
 
     /**
+     * @deprecated replaced by {@link PackageInstaller}
      * @hide
-     * Install a package. Since this may take a little while, the result will be
-     * posted back to the given observer. An installation will fail if the package named
-     * in the package file's manifest is already installed, or if there's no space
-     * available on the device.
-     * @param packageURI The location of the package file to install. This can be a 'file:' or a
-     * 'content:' URI.
-     * @param observer An observer callback to get notified when the package installation is
-     * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
-     * called when that happens. This parameter must not be null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that is performing the
-     * installation. This identifies which market the package came from.
-     * @param userId The user id.
      */
-     @RequiresPermission(anyOf = {
-            Manifest.permission.INSTALL_PACKAGES,
-            Manifest.permission.INTERACT_ACROSS_USERS_FULL})
-    public abstract void installPackageAsUser(
-            Uri packageURI, PackageInstallObserver observer, int flags,
-            String installerPackageName, int userId);
+    @Deprecated
+    public abstract void installPackageAsUser(Uri packageURI, PackageInstallObserver observer,
+            @InstallFlags int flags, String installerPackageName, @UserIdInt int userId);
 
     /**
-     * Similar to
-     * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
-     * with an extra verification file provided.
-     *
-     * @param packageURI The location of the package file to install. This can
-     *            be a 'file:' or a 'content:' URI.
-     * @param observer An observer callback to get notified when the package installation is
-     * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
-     * called when that happens. This parameter must not be null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     *            {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that
-     *            is performing the installation. This identifies which market
-     *            the package came from.
-     * @param verificationURI The location of the supplementary verification
-     *            file. This can be a 'file:' or a 'content:' URI. May be
-     *            {@code null}.
-     * @param manifestDigest an object that holds the digest of the package
-     *            which can be used to verify ownership. May be {@code null}.
-     * @param encryptionParams if the package to be installed is encrypted,
-     *            these parameters describing the encryption and authentication
-     *            used. May be {@code null}.
+     * @deprecated replaced by {@link PackageInstaller}
      * @hide
      */
+    @Deprecated
     public abstract void installPackageWithVerification(Uri packageURI,
-            PackageInstallObserver observer, int flags, String installerPackageName,
-            Uri verificationURI, ManifestDigest manifestDigest,
-            ContainerEncryptionParams encryptionParams);
+            PackageInstallObserver observer, @InstallFlags int flags, String installerPackageName,
+            Uri verificationURI, ContainerEncryptionParams encryptionParams);
 
     /**
-     * Similar to
-     * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but
-     * with an extra verification information provided.
-     *
-     * @param packageURI The location of the package file to install. This can
-     *            be a 'file:' or a 'content:' URI.
-     * @param observer An observer callback to get notified when the package installation is
-     * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be
-     * called when that happens. This parameter must not be null.
-     * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
-     *            {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
-     * @param installerPackageName Optional package name of the application that
-     *            is performing the installation. This identifies which market
-     *            the package came from.
-     * @param verificationParams an object that holds signal information to
-     *            assist verification. May be {@code null}.
-     * @param encryptionParams if the package to be installed is encrypted,
-     *            these parameters describing the encryption and authentication
-     *            used. May be {@code null}.
-     *
+     * @deprecated replaced by {@link PackageInstaller}
      * @hide
      */
+    @Deprecated
     public abstract void installPackageWithVerificationAndEncryption(Uri packageURI,
-            PackageInstallObserver observer, int flags, String installerPackageName,
+            PackageInstallObserver observer, @InstallFlags int flags, String installerPackageName,
             VerificationParams verificationParams, ContainerEncryptionParams encryptionParams);
 
     /**
@@ -3996,7 +4509,6 @@
      * on the system for other users, also install it for the calling user.
      * @hide
      */
-    // @SystemApi
     public abstract int installExistingPackage(String packageName) throws NameNotFoundException;
 
     /**
@@ -4007,7 +4519,7 @@
      @RequiresPermission(anyOf = {
             Manifest.permission.INSTALL_PACKAGES,
             Manifest.permission.INTERACT_ACROSS_USERS_FULL})
-    public abstract int installExistingPackageAsUser(String packageName, int userId)
+    public abstract int installExistingPackageAsUser(String packageName, @UserIdInt int userId)
             throws NameNotFoundException;
 
     /**
@@ -4105,7 +4617,7 @@
      *
      * @hide
      */
-    public abstract int getIntentVerificationStatus(String packageName, int userId);
+    public abstract int getIntentVerificationStatusAsUser(String packageName, @UserIdInt int userId);
 
     /**
      * Allow to change the status of a Intent Verification status for all IntentFilter of an App.
@@ -4127,8 +4639,8 @@
      *
      * @hide
      */
-    public abstract boolean updateIntentVerificationStatus(String packageName, int status,
-            int userId);
+    public abstract boolean updateIntentVerificationStatusAsUser(String packageName, int status,
+            @UserIdInt int userId);
 
     /**
      * Get the list of IntentFilterVerificationInfo for a specific package and User.
@@ -4168,7 +4680,8 @@
      *
      * @hide
      */
-    public abstract String getDefaultBrowserPackageName(int userId);
+    @TestApi
+    public abstract String getDefaultBrowserPackageNameAsUser(@UserIdInt int userId);
 
     /**
      * Set the default Browser package name for a specific user.
@@ -4182,7 +4695,8 @@
      *
      * @hide
      */
-    public abstract boolean setDefaultBrowserPackageName(String packageName, int userId);
+    public abstract boolean setDefaultBrowserPackageNameAsUser(String packageName,
+            @UserIdInt int userId);
 
     /**
      * Change the installer associated with a given package.  There are limitations
@@ -4203,45 +4717,44 @@
             String installerPackageName);
 
     /**
-     * Attempts to delete a package.  Since this may take a little while, the result will
-     * be posted back to the given observer.  A deletion will fail if the calling context
-     * lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
-     * named package cannot be found, or if the named package is a "system package".
-     * (TODO: include pointer to documentation on "system packages")
+     * Attempts to delete a package. Since this may take a little while, the
+     * result will be posted back to the given observer. A deletion will fail if
+     * the calling context lacks the
+     * {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
+     * named package cannot be found, or if the named package is a system
+     * package.
      *
      * @param packageName The name of the package to delete
-     * @param observer An observer callback to get notified when the package deletion is
-     * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
-     * called when that happens.  observer may be null to indicate that no callback is desired.
-     * @param flags Possible values: {@link #DELETE_KEEP_DATA},
-     * {@link #DELETE_ALL_USERS}.
-     *
+     * @param observer An observer callback to get notified when the package
+     *            deletion is complete.
+     *            {@link android.content.pm.IPackageDeleteObserver#packageDeleted}
+     *            will be called when that happens. observer may be null to
+     *            indicate that no callback is desired.
      * @hide
      */
-    // @SystemApi
-    public abstract void deletePackage(
-            String packageName, IPackageDeleteObserver observer, int flags);
+    public abstract void deletePackage(String packageName, IPackageDeleteObserver observer,
+            @DeleteFlags int flags);
 
     /**
-     * Attempts to delete a package.  Since this may take a little while, the result will
-     * be posted back to the given observer. A deletion will fail if the named package cannot be
-     * found, or if the named package is a "system package".
-     * (TODO: include pointer to documentation on "system packages")
+     * Attempts to delete a package. Since this may take a little while, the
+     * result will be posted back to the given observer. A deletion will fail if
+     * the named package cannot be found, or if the named package is a system
+     * package.
      *
      * @param packageName The name of the package to delete
-     * @param observer An observer callback to get notified when the package deletion is
-     * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
-     * called when that happens.  observer may be null to indicate that no callback is desired.
-     * @param flags Possible values: {@link #DELETE_KEEP_DATA}, {@link #DELETE_ALL_USERS}.
+     * @param observer An observer callback to get notified when the package
+     *            deletion is complete.
+     *            {@link android.content.pm.IPackageDeleteObserver#packageDeleted}
+     *            will be called when that happens. observer may be null to
+     *            indicate that no callback is desired.
      * @param userId The user Id
-     *
      * @hide
      */
      @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
             Manifest.permission.INTERACT_ACROSS_USERS_FULL})
-    public abstract void deletePackageAsUser(
-            String packageName, IPackageDeleteObserver observer, int flags, int userId);
+    public abstract void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
+            @DeleteFlags int flags, @UserIdInt int userId);
 
     /**
      * Retrieve the package name of the application that installed a package. This identifies
@@ -4308,7 +4821,6 @@
      *
      * @hide
      */
-    // @SystemApi
     public void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer) {
         freeStorageAndNotify(null, freeStorageSize, observer);
     }
@@ -4354,7 +4866,7 @@
      * should have the {@link android.Manifest.permission#GET_PACKAGE_SIZE} permission.
      *
      * @param packageName The name of the package whose size information is to be retrieved
-     * @param userHandle The user whose size information should be retrieved.
+     * @param userId The user whose size information should be retrieved.
      * @param observer An observer callback to get notified when the operation
      * is complete.
      * {@link android.content.pm.IPackageStatsObserver#onGetStatsCompleted(PackageStats, boolean)}
@@ -4365,17 +4877,17 @@
      *
      * @hide
      */
-    public abstract void getPackageSizeInfo(String packageName, int userHandle,
+    public abstract void getPackageSizeInfoAsUser(String packageName, @UserIdInt int userId,
             IPackageStatsObserver observer);
 
     /**
-     * Like {@link #getPackageSizeInfo(String, int, IPackageStatsObserver)}, but
+     * Like {@link #getPackageSizeInfoAsUser(String, int, IPackageStatsObserver)}, but
      * returns the size for the calling user.
      *
      * @hide
      */
     public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
-        getPackageSizeInfo(packageName, UserHandle.myUserId(), observer);
+        getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
     }
 
     /**
@@ -4400,30 +4912,38 @@
      * least preferred.
      *
      * @param flags Additional option flags. Use any combination of
-     * {@link #GET_ACTIVITIES},
-     * {@link #GET_GIDS},
-     * {@link #GET_CONFIGURATIONS},
-     * {@link #GET_INSTRUMENTATION},
-     * {@link #GET_PERMISSIONS},
-     * {@link #GET_PROVIDERS},
-     * {@link #GET_RECEIVERS},
-     * {@link #GET_SERVICES},
-     * {@link #GET_SIGNATURES}, to modify the data returned.
+     *         {@link #GET_ACTIVITIES}, {@link #GET_CONFIGURATIONS},
+     *         {@link #GET_GIDS}, {@link #GET_INSTRUMENTATION},
+     *         {@link #GET_INTENT_FILTERS}, {@link #GET_META_DATA},
+     *         {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS},
+     *         {@link #GET_RECEIVERS}, {@link #GET_SERVICES},
+     *         {@link #GET_SHARED_LIBRARY_FILES}, {@link #GET_SIGNATURES},
+     *         {@link #GET_URI_PERMISSION_PATTERNS}, {@link #GET_UNINSTALLED_PACKAGES},
+     *         {@link #MATCH_DISABLED_COMPONENTS}, {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS},
+     *         {@link #MATCH_UNINSTALLED_PACKAGES}
+     *         to modify the data returned.
      *
-     * @return Returns a list of PackageInfo objects describing each
-     * preferred application, in order of preference.
+     * @return A List of PackageInfo objects, one for each preferred application,
+     *         in order of preference.
      *
      * @see #GET_ACTIVITIES
-     * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
+     * @see #GET_GIDS
      * @see #GET_INSTRUMENTATION
+     * @see #GET_INTENT_FILTERS
+     * @see #GET_META_DATA
      * @see #GET_PERMISSIONS
      * @see #GET_PROVIDERS
      * @see #GET_RECEIVERS
      * @see #GET_SERVICES
+     * @see #GET_SHARED_LIBRARY_FILES
      * @see #GET_SIGNATURES
+     * @see #GET_URI_PERMISSION_PATTERNS
+     * @see #MATCH_DISABLED_COMPONENTS
+     * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
+     * @see #MATCH_UNINSTALLED_PACKAGES
      */
-    public abstract List<PackageInfo> getPreferredPackages(int flags);
+    public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
 
     /**
      * @deprecated This is a protected API that should not have been available
@@ -4454,8 +4974,8 @@
             to.
      * @hide
      */
-    public void addPreferredActivity(IntentFilter filter, int match,
-            ComponentName[] set, ComponentName activity, int userId) {
+    public void addPreferredActivityAsUser(IntentFilter filter, int match,
+            ComponentName[] set, ComponentName activity, @UserIdInt int userId) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
@@ -4489,7 +5009,7 @@
      */
     @Deprecated
     public void replacePreferredActivityAsUser(IntentFilter filter, int match,
-           ComponentName[] set, ComponentName activity, int userId) {
+           ComponentName[] set, ComponentName activity, @UserIdInt int userId) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
@@ -4684,27 +5204,6 @@
     public abstract boolean isSignedByExactly(String packageName, KeySet ks);
 
     /**
-     * Attempts to move package resources from internal to external media or vice versa.
-     * Since this may take a little while, the result will
-     * be posted back to the given observer.   This call may fail if the calling context
-     * lacks the {@link android.Manifest.permission#MOVE_PACKAGE} permission, if the
-     * named package cannot be found, or if the named package is a "system package".
-     *
-     * @param packageName The name of the package to delete
-     * @param observer An observer callback to get notified when the package move is
-     * complete. {@link android.content.pm.IPackageMoveObserver#packageMoved(boolean)} will be
-     * called when that happens.  observer may be null to indicate that no callback is desired.
-     * @param flags To indicate install location {@link #MOVE_INTERNAL} or
-     * {@link #MOVE_EXTERNAL_MEDIA}
-     *
-     * @hide
-     */
-    @Deprecated
-    public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Puts the package in a suspended state, making the package un-runnable,
      * but it doesn't remove the data or the actual package file. The application notifications
      * will be hidden and also the application will not show up in recents.
@@ -4717,7 +5216,7 @@
      * @hide
      */
     public abstract boolean setPackageSuspendedAsUser(
-            String packageName, boolean suspended, int userId);
+            String packageName, boolean suspended, @UserIdInt int userId);
 
     /** {@hide} */
     public static boolean isMoveStatusFinished(int status) {
@@ -4775,22 +5274,25 @@
     public abstract @NonNull PackageInstaller getPackageInstaller();
 
     /**
-     * Adds a {@link CrossProfileIntentFilter}. After calling this method all intents sent from the
-     * user with id sourceUserId can also be be resolved by activities in the user with id
-     * targetUserId if they match the specified intent filter.
+     * Adds a {@code CrossProfileIntentFilter}. After calling this method all
+     * intents sent from the user with id sourceUserId can also be be resolved
+     * by activities in the user with id targetUserId if they match the
+     * specified intent filter.
+     *
      * @param filter The {@link IntentFilter} the intent has to match
      * @param sourceUserId The source user id.
      * @param targetUserId The target user id.
-     * @param flags The possible values are {@link SKIP_CURRENT_PROFILE} and
-     *        {@link ONLY_IF_NO_MATCH_FOUND}.
+     * @param flags The possible values are {@link #SKIP_CURRENT_PROFILE} and
+     *            {@link #ONLY_IF_NO_MATCH_FOUND}.
      * @hide
      */
     public abstract void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId,
             int targetUserId, int flags);
 
     /**
-     * Clearing {@link CrossProfileIntentFilter}s which have the specified user as their
-     * source, and have been set by the app calling this method.
+     * Clearing {@code CrossProfileIntentFilter}s which have the specified user
+     * as their source, and have been set by the app calling this method.
+     *
      * @param sourceUserId The source user id.
      * @hide
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f445cf8..a6fec9f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -37,7 +37,6 @@
 import android.content.res.XmlResourceParser;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.FileUtils;
 import android.os.PatternMatcher;
 import android.os.Trace;
@@ -52,6 +51,8 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
+import android.util.jar.StrictJarFile;
 import android.view.Gravity;
 
 import com.android.internal.R;
@@ -86,8 +87,6 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.ZipEntry;
 
-import android.util.jar.StrictJarFile;
-
 /**
  * Parser for package files (APKs) on disk. This supports apps packaged either
  * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
@@ -421,8 +420,7 @@
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
             Set<String> grantedPermissions, PackageUserState state, int userId) {
-
-        if (!checkUseInstalledOrHidden(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state) || !p.isMatch(flags)) {
             return null;
         }
         PackageInfo pi = new PackageInfo();
@@ -466,92 +464,60 @@
                 p.featureGroups.toArray(pi.featureGroups);
             }
         }
-        if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
-            int N = p.activities.size();
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            final int N = p.activities.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.activities = new ActivityInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.activities.get(i).info.enabled) num++;
-                    }
-                    pi.activities = new ActivityInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Activity activity = p.activities.get(i);
-                    if (activity.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.activities.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
+                pi.activities = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_RECEIVERS) != 0) {
-            int N = p.receivers.size();
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            final int N = p.receivers.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.receivers = new ActivityInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.receivers.get(i).info.enabled) num++;
-                    }
-                    pi.receivers = new ActivityInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Activity activity = p.receivers.get(i);
-                    if (activity.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ActivityInfo[] res = new ActivityInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Activity a = p.receivers.get(i);
+                    if (state.isMatch(a.info, flags)) {
+                        res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
+                pi.receivers = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_SERVICES) != 0) {
-            int N = p.services.size();
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            final int N = p.services.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.services = new ServiceInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.services.get(i).info.enabled) num++;
-                    }
-                    pi.services = new ServiceInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Service service = p.services.get(i);
-                    if (service.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ServiceInfo[] res = new ServiceInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Service s = p.services.get(i);
+                    if (state.isMatch(s.info, flags)) {
+                        res[num++] = generateServiceInfo(s, flags, state, userId);
                     }
                 }
+                pi.services = ArrayUtils.trimToSize(res, num);
             }
         }
-        if ((flags&PackageManager.GET_PROVIDERS) != 0) {
-            int N = p.providers.size();
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            final int N = p.providers.size();
             if (N > 0) {
-                if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                    pi.providers = new ProviderInfo[N];
-                } else {
-                    int num = 0;
-                    for (int i=0; i<N; i++) {
-                        if (p.providers.get(i).info.enabled) num++;
-                    }
-                    pi.providers = new ProviderInfo[num];
-                }
-                for (int i=0, j=0; i<N; i++) {
-                    final Provider provider = p.providers.get(i);
-                    if (provider.info.enabled
-                        || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-                        pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
-                                state, userId);
+                int num = 0;
+                final ProviderInfo[] res = new ProviderInfo[N];
+                for (int i = 0; i < N; i++) {
+                    final Provider pr = p.providers.get(i);
+                    if (state.isMatch(pr.info, flags)) {
+                        res[num++] = generateProviderInfo(pr, flags, state, userId);
                     }
                 }
+                pi.providers = ArrayUtils.trimToSize(res, num);
             }
         }
         if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
@@ -1032,31 +998,6 @@
     }
 
     /**
-     * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the
-     * APK. If it successfully scanned the package and found the
-     * {@code AndroidManifest.xml}, {@code true} is returned.
-     */
-    public void collectManifestDigest(Package pkg) throws PackageParserException {
-        pkg.manifestDigest = null;
-
-        // TODO: extend to gather digest for split APKs
-        try {
-            final StrictJarFile jarFile = new StrictJarFile(pkg.baseCodePath);
-            try {
-                final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
-                if (je != null) {
-                    pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je));
-                }
-            } finally {
-                jarFile.close();
-            }
-        } catch (IOException | RuntimeException e) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
-                    "Failed to collect manifest digest");
-        }
-    }
-
-    /**
      * Collect certificates from all the APKs described in the given package,
      * populating {@link Package#mSignatures}. Also asserts that all APK
      * contents are signed correctly and consistently.
@@ -1082,11 +1023,56 @@
         final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0) && hasCode;
         final String apkPath = apkFile.getAbsolutePath();
 
+        // Try to verify the APK using APK Signature Scheme v2.
+        boolean verified = false;
+        {
+            Certificate[][] allSignersCerts = null;
+            Signature[] signatures = null;
+            try {
+                allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
+                signatures = convertToSignatures(allSignersCerts);
+                // APK verified using APK Signature Scheme v2.
+                verified = true;
+            } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
+                // No APK Signature Scheme v2 signature found
+            } catch (Exception e) {
+                // APK Signature Scheme v2 signature was found but did not verify
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                        "Failed to collect certificates from " + apkPath
+                                + " using APK Signature Scheme v2",
+                        e);
+            }
+
+            if (verified) {
+                if (pkg.mCertificates == null) {
+                    pkg.mCertificates = allSignersCerts;
+                    pkg.mSignatures = signatures;
+                    pkg.mSigningKeys = new ArraySet<>(allSignersCerts.length);
+                    for (int i = 0; i < allSignersCerts.length; i++) {
+                        Certificate[] signerCerts = allSignersCerts[i];
+                        Certificate signerCert = signerCerts[0];
+                        pkg.mSigningKeys.add(signerCert.getPublicKey());
+                    }
+                } else {
+                    if (!Signature.areExactMatch(pkg.mSignatures, signatures)) {
+                        throw new PackageParserException(
+                                INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                apkPath + " has mismatched certificates");
+                    }
+                }
+                // Not yet done, because we need to confirm that AndroidManifest.xml exists and,
+                // if requested, that classes.dex exists.
+            }
+        }
+
         boolean codeFound = false;
         StrictJarFile jarFile = null;
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
-            jarFile = new StrictJarFile(apkPath);
+            jarFile = new StrictJarFile(
+                    apkPath,
+                    !verified // whether to verify JAR signature
+                    );
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             // Always verify manifest, regardless of source
@@ -1096,6 +1082,16 @@
                         "Package " + apkPath + " has no manifest");
             }
 
+            // Optimization: early termination when APK already verified
+            if (verified) {
+                if ((requireCode) && (jarFile.findEntry(BYTECODE_FILENAME) == null)) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Package " + apkPath + " code is missing");
+                }
+                return;
+            }
+
+            // APK's integrity needs to be verified using JAR signature scheme.
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "buildVerifyList");
             final List<ZipEntry> toVerify = new ArrayList<>();
             toVerify.add(manifestEntry);
@@ -3223,12 +3219,19 @@
                 a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
             }
 
-            a.info.resizeable = sa.getBoolean(
-                    R.styleable.AndroidManifestActivity_resizeableActivity,
-                    owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+                    owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N)) {
+                a.info.flags |= ActivityInfo.FLAG_RESIZEABLE;
 
-            a.info.supportsPip = a.info.resizeable ? sa.getBoolean(
-                    R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false;
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+                        false)) {
+                    a.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+                }
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
+                a.info.flags |= ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+            }
 
             a.info.screenOrientation = sa.getInt(
                     R.styleable.AndroidManifestActivity_screenOrientation,
@@ -3939,6 +3942,11 @@
             s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
         }
         if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_externalService,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
+        }
+        if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestService_singleUser,
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
@@ -4492,12 +4500,6 @@
         /* The required account type without which this application will not function */
         public String mRequiredAccountType;
 
-        /**
-         * Digest suitable for comparing whether this package's manifest is the
-         * same as another.
-         */
-        public ManifestDigest manifestDigest;
-
         public String mOverlayTarget;
         public int mOverlayPriority;
         public boolean mTrustedOverlay;
@@ -4647,6 +4649,13 @@
                     && !isForwardLocked() && !applicationInfo.isExternalAsec();
         }
 
+        public boolean isMatch(int flags) {
+            if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+                return isSystemApp();
+            }
+            return true;
+        }
+
         public String toString() {
             return "Package{"
                 + Integer.toHexString(System.identityHashCode(this))
@@ -4897,7 +4906,7 @@
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
-        if (!checkUseInstalledOrHidden(flags, state)) {
+        if (!checkUseInstalledOrHidden(flags, state) || !p.isMatch(flags)) {
             return null;
         }
         if (!copyNeeded(flags, p, state, null, userId)
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 91fdf7f..38e0044 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -17,9 +17,20 @@
 package android.content.pm;
 
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.util.ArraySet;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * Per-user state information about a package.
  * @hide
@@ -58,12 +69,77 @@
         hidden = o.hidden;
         suspended = o.suspended;
         lastDisableAppCaller = o.lastDisableAppCaller;
-        disabledComponents = o.disabledComponents != null
-                ? new ArraySet<>(o.disabledComponents) : null;
-        enabledComponents = o.enabledComponents != null
-                ? new ArraySet<>(o.enabledComponents) : null;
+        disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
+        enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
         blockUninstall = o.blockUninstall;
         domainVerificationStatus = o.domainVerificationStatus;
         appLinkGeneration = o.appLinkGeneration;
     }
+
+    /**
+     * Test if this package is installed.
+     */
+    public boolean isInstalled(int flags) {
+        return (this.installed && !this.hidden)
+                || (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+    }
+
+    /**
+     * Test if the given component is considered installed, enabled and a match
+     * for the given flags.
+     */
+    public boolean isMatch(ComponentInfo componentInfo, int flags) {
+        if (!isInstalled(flags)) return false;
+        if (!isEnabled(componentInfo, flags)) return false;
+
+        if ((flags & MATCH_SYSTEM_ONLY) != 0) {
+            if (!componentInfo.applicationInfo.isSystemApp()) {
+                return false;
+            }
+        }
+
+        final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
+                && !componentInfo.encryptionAware;
+        final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
+                && componentInfo.encryptionAware;
+        return matchesUnaware || matchesAware;
+    }
+
+    /**
+     * Test if the given component is considered enabled.
+     */
+    public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+        if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+            return true;
+        }
+
+        // First check if the overall package is disabled; if the package is
+        // enabled then fall through to check specific component
+        switch (this.enabled) {
+            case COMPONENT_ENABLED_STATE_DISABLED:
+            case COMPONENT_ENABLED_STATE_DISABLED_USER:
+                return false;
+            case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+                if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+                    return false;
+                }
+            case COMPONENT_ENABLED_STATE_DEFAULT:
+                if (!componentInfo.applicationInfo.enabled) {
+                    return false;
+                }
+            case COMPONENT_ENABLED_STATE_ENABLED:
+                break;
+        }
+
+        // Check if component has explicit state before falling through to
+        // the manifest default
+        if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) {
+            return true;
+        }
+        if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) {
+            return false;
+        }
+
+        return componentInfo.enabled;
+    }
 }
diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java
index cfb4473..945858e6 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -50,6 +51,10 @@
 
     private final List<T> mList;
 
+    public static <T extends Parcelable> ParceledListSlice<T> emptyList() {
+        return new ParceledListSlice<T>(Collections.<T> emptyList());
+    }
+
     public ParceledListSlice(List<T> list) {
         mList = list;
     }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index d5d3007..c9be6ed 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -172,7 +172,8 @@
      */
     public boolean handleAllWebDataURI;
 
-    private ComponentInfo getComponentInfo() {
+    /** {@hide} */
+    public ComponentInfo getComponentInfo() {
         if (activityInfo != null) return activityInfo;
         if (serviceInfo != null) return serviceInfo;
         if (providerInfo != null) return providerInfo;
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 74e5c2a..eecf0de 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -49,6 +49,14 @@
     public static final int FLAG_ISOLATED_PROCESS = 0x0002;
 
     /**
+     * Bit in {@link #flags}: If set, the service can be bound and run in the
+     * calling application's package, rather than the package in which it is
+     * declared.  Set from {@link android.R.attr#externalService} attribute.
+     * @hide
+     */
+    public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
+
+    /**
      * Bit in {@link #flags}: If set, a single instance of the service will
      * run for all users on the device.  Set from the
      * {@link android.R.attr#singleUser} attribute.
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index e3050fe..9cf4675 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -145,6 +145,10 @@
         return (flags & FLAG_EPHEMERAL) == FLAG_EPHEMERAL;
     }
 
+    public boolean isInitialized() {
+        return (flags & FLAG_INITIALIZED) == FLAG_INITIALIZED;
+    }
+
     /**
      * Returns true if the user is a split system user.
      * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java
index e5119b6..f90d295 100644
--- a/core/java/android/content/pm/VerificationParams.java
+++ b/core/java/android/content/pm/VerificationParams.java
@@ -16,7 +16,6 @@
 
 package android.content.pm;
 
-import android.content.pm.ManifestDigest;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -51,12 +50,6 @@
     private int mInstallerUid;
 
     /**
-     * An object that holds the digest of the package which can be used to
-     * verify ownership.
-     */
-    private final ManifestDigest mManifestDigest;
-
-    /**
      * Creates verification specifications for installing with application verification.
      *
      * @param verificationURI The location of the supplementary verification
@@ -67,16 +60,13 @@
      *            May be {@code null}.
      * @param originatingUid UID of the application that the install request originated
      *            from, or NO_UID if not present
-     * @param manifestDigest an object that holds the digest of the package
-     *            which can be used to verify ownership. May be {@code null}.
      */
     public VerificationParams(Uri verificationURI, Uri originatingURI, Uri referrer,
-            int originatingUid, ManifestDigest manifestDigest) {
+            int originatingUid) {
         mVerificationURI = verificationURI;
         mOriginatingURI = originatingURI;
         mReferrer = referrer;
         mOriginatingUid = originatingUid;
-        mManifestDigest = manifestDigest;
         mInstallerUid = NO_UID;
     }
 
@@ -97,10 +87,6 @@
         return mOriginatingUid;
     }
 
-    public ManifestDigest getManifestDigest() {
-        return mManifestDigest;
-    }
-
     /** @return NO_UID when not set */
     public int getInstallerUid() {
         return mInstallerUid;
@@ -155,14 +141,6 @@
             return false;
         }
 
-        if (mManifestDigest == null) {
-            if (other.mManifestDigest != null) {
-                return false;
-            }
-        } else if (!mManifestDigest.equals(other.mManifestDigest)) {
-            return false;
-        }
-
         if (mInstallerUid != other.mInstallerUid) {
             return false;
         }
@@ -178,8 +156,7 @@
         hash += 7 * (mOriginatingURI == null ? 1 : mOriginatingURI.hashCode());
         hash += 11 * (mReferrer == null ? 1 : mReferrer.hashCode());
         hash += 13 * mOriginatingUid;
-        hash += 17 * (mManifestDigest == null ? 1 : mManifestDigest.hashCode());
-        hash += 19 * mInstallerUid;
+        hash += 17 * mInstallerUid;
 
         return hash;
     }
@@ -196,8 +173,6 @@
         sb.append(mReferrer.toString());
         sb.append(",mOriginatingUid=");
         sb.append(mOriginatingUid);
-        sb.append(",mManifestDigest=");
-        sb.append(mManifestDigest.toString());
         sb.append(",mInstallerUid=");
         sb.append(mInstallerUid);
         sb.append('}');
@@ -211,7 +186,6 @@
         dest.writeParcelable(mOriginatingURI, 0);
         dest.writeParcelable(mReferrer, 0);
         dest.writeInt(mOriginatingUid);
-        dest.writeParcelable(mManifestDigest, 0);
         dest.writeInt(mInstallerUid);
     }
 
@@ -221,7 +195,6 @@
         mOriginatingURI = source.readParcelable(Uri.class.getClassLoader());
         mReferrer = source.readParcelable(Uri.class.getClassLoader());
         mOriginatingUid = source.readInt();
-        mManifestDigest = source.readParcelable(ManifestDigest.class.getClassLoader());
         mInstallerUid = source.readInt();
     }
 
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 7669053..7b0b98d 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -16,6 +16,11 @@
 
 package android.content.res;
 
+import android.annotation.AnyRes;
+import android.annotation.ArrayRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 import android.util.SparseArray;
@@ -142,80 +147,95 @@
     }
 
     /**
-     * Retrieve the string value associated with a particular resource
-     * identifier for the current configuration / skin.
+     * Retrieves the string value associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @return the string value, or {@code null}
      */
-    /*package*/ final CharSequence getResourceText(int ident) {
+    @Nullable
+    final CharSequence getResourceText(@StringRes int resId) {
         synchronized (this) {
-            TypedValue tmpValue = mValue;
-            int block = loadResourceValue(ident, (short) 0, tmpValue, true);
-            if (block >= 0) {
-                if (tmpValue.type == TypedValue.TYPE_STRING) {
-                    return mStringBlocks[block].get(tmpValue.data);
-                }
-                return tmpValue.coerceToString();
+            final TypedValue outValue = mValue;
+            if (getResourceValue(resId, 0, outValue, true)) {
+                return outValue.coerceToString();
             }
+            return null;
         }
-        return null;
     }
 
     /**
-     * Retrieve the string value associated with a particular resource
-     * identifier for the current configuration / skin.
+     * Retrieves the string value associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @param bagEntryId
+     * @return the string value, or {@code null}
      */
-    /*package*/ final CharSequence getResourceBagText(int ident, int bagEntryId) {
+    @Nullable
+    final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
         synchronized (this) {
-            TypedValue tmpValue = mValue;
-            int block = loadResourceBagValue(ident, bagEntryId, tmpValue, true);
-            if (block >= 0) {
-                if (tmpValue.type == TypedValue.TYPE_STRING) {
-                    return mStringBlocks[block].get(tmpValue.data);
-                }
-                return tmpValue.coerceToString();
+            final TypedValue outValue = mValue;
+            final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
+            if (block < 0) {
+                return null;
             }
+            if (outValue.type == TypedValue.TYPE_STRING) {
+                return mStringBlocks[block].get(outValue.data);
+            }
+            return outValue.coerceToString();
         }
-        return null;
     }
 
     /**
-     * Retrieve the string array associated with a particular resource
-     * identifier.
-     * @param id Resource id of the string array
+     * Retrieves the string array associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier of the string array
+     * @return the string array, or {@code null}
      */
-    /*package*/ final String[] getResourceStringArray(final int id) {
-        String[] retArray = getArrayStringResource(id);
-        return retArray;
+    @Nullable
+    final String[] getResourceStringArray(@ArrayRes int resId) {
+        return getArrayStringResource(resId);
     }
 
-
-    /*package*/ final boolean getResourceValue(int ident,
-                                               int density,
-                                               TypedValue outValue,
-                                               boolean resolveRefs)
-    {
-        int block = loadResourceValue(ident, (short) density, outValue, resolveRefs);
-        if (block >= 0) {
-            if (outValue.type != TypedValue.TYPE_STRING) {
-                return true;
-            }
+    /**
+     * Populates {@code outValue} with the data associated a particular
+     * resource identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @param densityDpi the density bucket for which to load the resource
+     * @param outValue the typed value in which to put the data
+     * @param resolveRefs {@code true} to resolve references, {@code false}
+     *                    to leave them unresolved
+     * @return {@code true} if the data was loaded into {@code outValue},
+     *         {@code false} otherwise
+     */
+    final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+            boolean resolveRefs) {
+        final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
+        if (block < 0) {
+            return false;
+        }
+        if (outValue.type == TypedValue.TYPE_STRING) {
             outValue.string = mStringBlocks[block].get(outValue.data);
-            return true;
         }
-        return false;
+        return true;
     }
 
     /**
      * Retrieve the text array associated with a particular resource
      * identifier.
-     * @param id Resource id of the string array
+     *
+     * @param resId the resource id of the string array
      */
-    /*package*/ final CharSequence[] getResourceTextArray(final int id) {
-        int[] rawInfoArray = getArrayStringInfo(id);
-        int rawInfoArrayLen = rawInfoArray.length;
+    final CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+        final int[] rawInfoArray = getArrayStringInfo(resId);
+        final int rawInfoArrayLen = rawInfoArray.length;
         final int infoArrayLen = rawInfoArrayLen / 2;
         int block;
         int index;
-        CharSequence[] retArray = new CharSequence[infoArrayLen];
+        final CharSequence[] retArray = new CharSequence[infoArrayLen];
         for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
             block = rawInfoArray[i];
             index = rawInfoArray[i + 1];
@@ -223,32 +243,45 @@
         }
         return retArray;
     }
-    
-    /*package*/ final boolean getThemeValue(long theme, int ident,
-            TypedValue outValue, boolean resolveRefs) {
-        int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs);
-        if (block >= 0) {
-            if (outValue.type != TypedValue.TYPE_STRING) {
-                return true;
-            }
-            StringBlock[] blocks = mStringBlocks;
-            if (blocks == null) {
-                ensureStringBlocks();
-                blocks = mStringBlocks;
-            }
-            outValue.string = blocks[block].get(outValue.data);
-            return true;
+
+    /**
+     * Populates {@code outValue} with the data associated with a particular
+     * resource identifier for the current configuration. Resolves theme
+     * attributes against the specified theme.
+     *
+     * @param theme the native pointer of the theme
+     * @param resId the resource identifier to load
+     * @param outValue the typed value in which to put the data
+     * @param resolveRefs {@code true} to resolve references, {@code false}
+     *                    to leave them unresolved
+     * @return {@code true} if the data was loaded into {@code outValue},
+     *         {@code false} otherwise
+     */
+    final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+            boolean resolveRefs) {
+        final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs);
+        if (block < 0) {
+            return false;
         }
-        return false;
+        if (outValue.type == TypedValue.TYPE_STRING) {
+            final StringBlock[] blocks = ensureStringBlocks();
+            outValue.string = blocks[block].get(outValue.data);
+        }
+        return true;
     }
 
-    /*package*/ final void ensureStringBlocks() {
-        if (mStringBlocks == null) {
-            synchronized (this) {
-                if (mStringBlocks == null) {
-                    makeStringBlocks(sSystem.mStringBlocks);
-                }
+    /**
+     * Ensures the string blocks are loaded.
+     *
+     * @return the string blocks
+     */
+    @NonNull
+    final StringBlock[] ensureStringBlocks() {
+        synchronized (this) {
+            if (mStringBlocks == null) {
+                makeStringBlocks(sSystem.mStringBlocks);
             }
+            return mStringBlocks;
         }
     }
 
@@ -698,6 +731,18 @@
      */
     public native final String[] getLocales();
 
+    /**
+     * Same as getLocales(), except that locales that are only provided by the system (i.e. those
+     * present in framework-res.apk or its overlays) will not be listed.
+     *
+     * For example, if the "system" assets support English, French, and German, and the additional
+     * assets support Cherokee and French, getLocales() would return
+     * [Cherokee, English, French, German], while getNonSystemLocales() would return
+     * [Cherokee, French].
+     * {@hide}
+     */
+    public native final String[] getNonSystemLocales();
+
     /** {@hide} */
     public native final Configuration[] getSizeConfigurations();
 
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 60c6e82..7b56eeb 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -151,8 +151,9 @@
 
     private boolean mPreloading;
 
+    // Cyclical cache used for recently-accessed XML files.
     private int mLastCachedXmlBlockIndex = -1;
-    private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
+    private final String[] mCachedXmlBlockFiles = new String[4];
     private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
 
     final AssetManager mAssets;
@@ -866,6 +867,11 @@
         try {
             getValueForDensity(id, density, value, true);
 
+            // If the drawable's XML lives in our current density qualifier,
+            // it's okay to use a scaled version from the cache. Otherwise, we
+            // need to actually load the drawable from XML.
+            final boolean useCache = value.density == mMetrics.densityDpi;
+
             /*
              * Pretend the requested density is actually the display density. If
              * the drawable returned is not the requested density, then force it
@@ -881,7 +887,7 @@
                 }
             }
 
-            return loadDrawable(value, id, theme);
+            return loadDrawable(value, id, theme, useCache);
         } finally {
             releaseTempTypedValue(value);
         }
@@ -1976,7 +1982,21 @@
 
             if (setLocalesToDefault || mResolvedLocale == null
                     || (configChanges & Configuration.NATIVE_CONFIG_LOCALE) != 0) {
-                mResolvedLocale = locales.getFirstMatch(mAssets.getLocales());
+                if (locales.size() == 1) {
+                    // This is an optimization to avoid the JNI call(s) when the result of
+                    // getFirstMatchWithEnglishSupported() does not depend on the supported locales.
+                    mResolvedLocale = locales.getPrimary();
+                } else {
+                    String[] supportedLocales = mAssets.getNonSystemLocales();
+                    if (LocaleList.isPseudoLocalesOnly(supportedLocales)) {
+                        // We fallback to all locales (including system locales) if there was no
+                        // locale specifically supported by the assets. This is to properly support
+                        // apps that only rely on the shared system assets and don't need assets of
+                        // their own.
+                        supportedLocales = mAssets.getLocales();
+                    }
+                    mResolvedLocale = locales.getFirstMatchWithEnglishSupported(supportedLocales);
+                }
             }
             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
                     adjustLanguageTag(mResolvedLocale.toLanguageTag()),
@@ -2351,16 +2371,17 @@
      * tools.
      */
     public final void flushLayoutCache() {
-        synchronized (mCachedXmlBlockIds) {
-            // First see if this block is in our cache.
-            final int num = mCachedXmlBlockIds.length;
-            for (int i=0; i<num; i++) {
-                mCachedXmlBlockIds[i] = -0;
-                XmlBlock oldBlock = mCachedXmlBlocks[i];
+        final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
+        final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+        synchronized (cachedXmlBlockFiles) {
+            final int num = cachedXmlBlockFiles.length;
+            for (int i = 0; i < num; i++) {
+                final XmlBlock oldBlock = cachedXmlBlocks[i];
                 if (oldBlock != null) {
                     oldBlock.close();
                 }
-                mCachedXmlBlocks[i] = null;
+                cachedXmlBlockFiles[i] = null;
+                cachedXmlBlocks[i] = null;
             }
         }
     }
@@ -2437,6 +2458,12 @@
 
     @Nullable
     Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
+        return loadDrawable(value, id, theme, true);
+    }
+
+    @Nullable
+    Drawable loadDrawable(TypedValue value, int id, Theme theme, boolean useCache)
+            throws NotFoundException {
         try {
             if (TRACE_FOR_PRELOAD) {
                 // Log only framework resources
@@ -2463,16 +2490,17 @@
             }
 
             // First, check whether we have a cached version of this drawable
-            // that was inflated against the specified theme.
-            if (!mPreloading) {
+            // that was inflated against the specified theme. Skip the cache if
+            // we're currently preloading or we're not using the cache.
+            if (!mPreloading && useCache) {
                 final Drawable cachedDrawable = caches.getInstance(key, theme);
                 if (cachedDrawable != null) {
                     return cachedDrawable;
                 }
             }
 
-            // Next, check preloaded drawables. These may contain unresolved theme
-            // attributes.
+            // Next, check preloaded drawables. Preloaded drawables may contain
+            // unresolved theme attributes.
             final ConstantState cs;
             if (isColorDrawable) {
                 cs = sPreloadedColorDrawables.get(key);
@@ -2500,8 +2528,9 @@
             }
 
             // If we were able to obtain a drawable, store it in the appropriate
-            // cache: preload, not themed, null theme, or theme-specific.
-            if (dr != null) {
+            // cache: preload, not themed, null theme, or theme-specific. Don't
+            // pollute the cache with drawables loaded from a foreign density.
+            if (dr != null && useCache) {
                 dr.setChangingConfigurations(value.changingConfigurations);
                 cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
             }
@@ -2730,7 +2759,16 @@
         return csl;
     }
 
-    /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
+    /**
+     * Loads an XML parser for the specified file.
+     *
+     * @param id the resource identifier for the file
+     * @param type the type of resource (used for logging)
+     * @return a parser for the specified XML file
+     * @throws NotFoundException if the file could not be loaded
+     */
+    @NonNull
+    XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
             throws NotFoundException {
         final TypedValue value = obtainTempTypedValue(id);
         try {
@@ -2744,53 +2782,58 @@
             releaseTempTypedValue(value);
         }
     }
-    
-    /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
-            int assetCookie, String type) throws NotFoundException {
+
+    /**
+     * Loads an XML parser for the specified file.
+     *
+     * @param file the path for the XML file to parse
+     * @param id the resource identifier for the file
+     * @param assetCookie the asset cookie for the file
+     * @param type the type of resource (used for logging)
+     * @return a parser for the specified XML file
+     * @throws NotFoundException if the file could not be loaded
+     */
+    @NonNull
+    XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id,
+            int assetCookie, @NonNull String type) throws NotFoundException {
         if (id != 0) {
             try {
-                // These may be compiled...
-                synchronized (mCachedXmlBlockIds) {
+                final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
+                final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+                synchronized (cachedXmlBlockFiles) {
                     // First see if this block is in our cache.
-                    final int num = mCachedXmlBlockIds.length;
-                    for (int i=0; i<num; i++) {
-                        if (mCachedXmlBlockIds[i] == id) {
-                            //System.out.println("**** REUSING XML BLOCK!  id="
-                            //                   + id + ", index=" + i);
-                            return mCachedXmlBlocks[i].newParser();
+                    final int num = cachedXmlBlockFiles.length;
+                    for (int i = 0; i < num; i++) {
+                        if (cachedXmlBlockFiles[i] != null
+                                && cachedXmlBlockFiles[i].equals(file)) {
+                            return cachedXmlBlocks[i].newParser();
                         }
                     }
 
                     // Not in the cache, create a new block and put it at
                     // the next slot in the cache.
-                    XmlBlock block = mAssets.openXmlBlockAsset(
-                            assetCookie, file);
+                    final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
                     if (block != null) {
-                        int pos = mLastCachedXmlBlockIndex+1;
-                        if (pos >= num) pos = 0;
+                        final int pos = (mLastCachedXmlBlockIndex + 1) % num;
                         mLastCachedXmlBlockIndex = pos;
-                        XmlBlock oldBlock = mCachedXmlBlocks[pos];
+                        final XmlBlock oldBlock = cachedXmlBlocks[pos];
                         if (oldBlock != null) {
                             oldBlock.close();
                         }
-                        mCachedXmlBlockIds[pos] = id;
-                        mCachedXmlBlocks[pos] = block;
-                        //System.out.println("**** CACHING NEW XML BLOCK!  id="
-                        //                   + id + ", index=" + pos);
+                        cachedXmlBlockFiles[pos] = file;
+                        cachedXmlBlocks[pos] = block;
                         return block.newParser();
                     }
                 }
             } catch (Exception e) {
-                NotFoundException rnf = new NotFoundException(
-                        "File " + file + " from xml type " + type + " resource ID #0x"
-                        + Integer.toHexString(id));
+                final NotFoundException rnf = new NotFoundException("File " + file
+                        + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
                 rnf.initCause(e);
                 throw rnf;
             }
         }
 
-        throw new NotFoundException(
-                "File " + file + " from xml type " + type + " resource ID #0x"
+        throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
                 + Integer.toHexString(id));
     }
 
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index e875864..6935174 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -534,6 +534,24 @@
     public static final String STRING_TYPE_WRIST_TILT_GESTURE = "android.sensor.wrist_tilt_gesture";
 
     /**
+     * The current orientation of the device.
+     * <p>
+     * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+     *
+     * @hide Expected to be used internally for auto-rotate and speaker rotation.
+     *
+     */
+    public static final int TYPE_DEVICE_ORIENTATION = 27;
+
+    /**
+     * A constant string describing a device orientation sensor type.
+     *
+     * @hide
+     * @see #TYPE_DEVICE_ORIENTATION
+     */
+    public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation";
+
+    /**
      * A constant describing all sensor types.
      */
     public static final int TYPE_ALL = -1;
@@ -618,6 +636,7 @@
             1, // SENSOR_TYPE_GLANCE_GESTURE
             1, // SENSOR_TYPE_PICK_UP_GESTURE
             1, // SENSOR_TYPE_WRIST_TILT_GESTURE
+            1, // SENSOR_TYPE_DEVICE_ORIENTATION
     };
 
     /**
@@ -939,6 +958,9 @@
             case TYPE_TEMPERATURE:
                 mStringType = STRING_TYPE_TEMPERATURE;
                 return true;
+            case TYPE_DEVICE_ORIENTATION:
+                mStringType = STRING_TYPE_DEVICE_ORIENTATION;
+                return true;
             default:
                 return false;
         }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 906c2a19..9937b2c 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -483,6 +483,20 @@
      * on it. In earlier versions, this used to be always 3 which has changed now. </p>
      *
      * @see GeomagneticField
+     *
+     * <h4> {@link android.hardware.Sensor#TYPE_DEVICE_ORIENTATION
+     * Sensor.TYPE_DEVICE_ORIENTATION}:</h4>
+     * The current device orientation will be available in values[0]. The only
+     * available values are:
+     * <ul>
+     * <li> 0: device is in default orientation (Y axis is vertical and points up)
+     * <li> 1: device is rotated 90 degrees counter-clockwise from default
+     *         orientation (X axis is vertical and points up)
+     * <li> 2: device is rotated 180 degrees from default orientation (Y axis is
+     *         vertical and points down)
+     * <li> 3: device is rotated 90 degrees clockwise from default orientation (X axis
+     *         is vertical and points down)
+     * </ul>
      */
     public final float[] values;
 
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 8f45f72..852a4d9 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -168,7 +168,7 @@
      * <p>The active capture session determines the set of potential output Surfaces for
      * the camera device for each capture request. A given request may use all
      * or only some of the outputs. Once the CameraCaptureSession is created, requests can be
-     * can be submitted with {@link CameraCaptureSession#capture capture},
+     * submitted with {@link CameraCaptureSession#capture capture},
      * {@link CameraCaptureSession#captureBurst captureBurst},
      * {@link CameraCaptureSession#setRepeatingRequest setRepeatingRequest}, or
      * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</p>
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 6e02df1..c3a4954 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -89,7 +89,6 @@
             new SparseArray<CaptureCallbackHolder>();
 
     private int mRepeatingRequestId = REQUEST_ID_NONE;
-    private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
     // Map stream IDs to input/output configurations
     private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
             new SimpleEntry<>(REQUEST_ID_NONE, null);
@@ -916,11 +915,6 @@
                 int requestId = mRepeatingRequestId;
                 mRepeatingRequestId = REQUEST_ID_NONE;
 
-                // Queue for deletion after in-flight requests finish
-                if (mCaptureCallbackMap.get(requestId) != null) {
-                    mRepeatingRequestIdDeletedList.add(requestId);
-                }
-
                 try {
                     LongParcelable lastFrameNumberRef = new LongParcelable();
                     mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index b8f464d..5d969b1 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -49,11 +49,12 @@
 
     // Keyboard layouts configuration.
     KeyboardLayout[] getKeyboardLayouts();
+    KeyboardLayout[] getKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier);
     KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
     String getCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier);
     void setCurrentKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor);
-    String[] getKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier);
+    String[] getEnabledKeyboardLayoutsForInputDevice(in InputDeviceIdentifier identifier);
     void addKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
             String keyboardLayoutDescriptor);
     void removeKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 16b8722..9972f49 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -474,6 +474,29 @@
     }
 
     /**
+     * Gets information about all supported keyboard layouts appropriate
+     * for a specific input device.
+     * <p>
+     * The input manager consults the built-in keyboard layouts as well
+     * as all keyboard layouts advertised by applications using a
+     * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
+     * </p>
+     *
+     * @return A list of all supported keyboard layouts for a specific
+     * input device.
+     *
+     * @hide
+     */
+    public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+        try {
+            return mIm.getKeyboardLayoutsForInputDevice(identifier);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not get list of keyboard layouts for input device.", ex);
+            return new KeyboardLayout[0];
+        }
+    }
+
+    /**
      * Gets the keyboard layout with the specified descriptor.
      *
      * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by
@@ -551,13 +574,13 @@
      * @return The keyboard layout descriptors.
      * @hide
      */
-    public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+    public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
         if (identifier == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         try {
-            return mIm.getKeyboardLayoutsForInputDevice(identifier);
+            return mIm.getEnabledKeyboardLayoutsForInputDevice(identifier);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not get keyboard layouts for input device.", ex);
             return ArrayUtils.emptyArray(String.class);
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index ed51402..584008c 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Locale;
+
 /**
  * Describes a keyboard layout.
  *
@@ -30,6 +32,9 @@
     private final String mLabel;
     private final String mCollection;
     private final int mPriority;
+    private final Locale[] mLocales;
+    private final int mVendorId;
+    private final int mProductId;
 
     public static final Parcelable.Creator<KeyboardLayout> CREATOR =
             new Parcelable.Creator<KeyboardLayout>() {
@@ -41,11 +46,19 @@
         }
     };
 
-    public KeyboardLayout(String descriptor, String label, String collection, int priority) {
+    public KeyboardLayout(String descriptor, String label, String collection, int priority,
+            Locale[] locales, int vid, int pid) {
         mDescriptor = descriptor;
         mLabel = label;
         mCollection = collection;
         mPriority = priority;
+        if (locales != null) {
+            mLocales = locales;
+        } else {
+            mLocales = new Locale[0];
+        }
+        mVendorId = vid;
+        mProductId = pid;
     }
 
     private KeyboardLayout(Parcel source) {
@@ -53,6 +66,13 @@
         mLabel = source.readString();
         mCollection = source.readString();
         mPriority = source.readInt();
+        int N = source.readInt();
+        mLocales = new Locale[N];
+        for (int i = 0; i < N; i++) {
+            mLocales[i] = Locale.forLanguageTag(source.readString());
+        }
+        mVendorId = source.readInt();
+        mProductId = source.readInt();
     }
 
     /**
@@ -83,6 +103,33 @@
         return mCollection;
     }
 
+    /**
+     * Gets the locales that this keyboard layout is intended for.
+     * This may be empty if a locale has not been assigned to this keyboard layout.
+     * @return The keyboard layout's intended locale.
+     */
+    public Locale[] getLocales() {
+        return mLocales;
+    }
+
+    /**
+     * Gets the vendor ID of the hardware device this keyboard layout is intended for.
+     * Returns -1 if this is not specific to any piece of hardware.
+     * @return The hardware vendor ID of the keyboard layout's intended device.
+     */
+    public int getVendorId() {
+        return mVendorId;
+    }
+
+    /**
+     * Gets the product ID of the hardware device this keyboard layout is intended for.
+     * Returns -1 if this is not specific to any piece of hardware.
+     * @return The hardware product ID of the keyboard layout's intended device.
+     */
+    public int getProductId() {
+        return mProductId;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -94,6 +141,16 @@
         dest.writeString(mLabel);
         dest.writeString(mCollection);
         dest.writeInt(mPriority);
+        if (mLocales != null) {
+            dest.writeInt(mLocales.length);
+            for (Locale l : mLocales) {
+                dest.writeString(l.toLanguageTag());
+            }
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(mVendorId);
+        dest.writeInt(mProductId);
     }
 
     @Override
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8ab8991..2826882 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -19,16 +19,22 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.database.ContentObserver;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
@@ -68,6 +74,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * InputMethodService provides a standard implementation of an InputMethod,
@@ -634,6 +642,97 @@
     }
 
     /**
+     * A {@link ContentObserver} to monitor {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD}.
+     *
+     * <p>Note that {@link Settings.Secure#SHOW_IME_WITH_HARD_KEYBOARD} is not a public API.
+     * Basically this functionality still needs to be considered as implementation details.</p>
+     */
+    @MainThread
+    private static final class SettingsObserver extends ContentObserver {
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({
+                ShowImeWithHardKeyboardType.UNKNOWN,
+                ShowImeWithHardKeyboardType.FALSE,
+                ShowImeWithHardKeyboardType.TRUE,
+        })
+        private @interface ShowImeWithHardKeyboardType {
+            int UNKNOWN = 0;
+            int FALSE = 1;
+            int TRUE = 2;
+        }
+        @ShowImeWithHardKeyboardType
+        private int mShowImeWithHardKeyboard = ShowImeWithHardKeyboardType.UNKNOWN;
+
+        private final InputMethodService mService;
+
+        private SettingsObserver(InputMethodService service) {
+            super(new Handler(service.getMainLooper()));
+            mService = service;
+        }
+
+        /**
+         * A factory method that internally enforces two-phase initialization to make sure that the
+         * object reference will not be escaped until the object is properly constructed.
+         *
+         * <p>NOTE: Currently {@link SettingsObserver} is accessed only from main thread.  Hence
+         * this enforcement of two-phase initialization may be unnecessary at the moment.</p>
+         *
+         * @param service {@link InputMethodService} that needs to receive the callback.
+         * @return {@link SettingsObserver} that is already registered to
+         * {@link android.content.ContentResolver}. The caller must call
+         * {@link SettingsObserver#unregister()}.
+         */
+        public static SettingsObserver createAndRegister(InputMethodService service) {
+            final SettingsObserver observer = new SettingsObserver(service);
+            // The observer is properly constructed. Let's start accepting the event.
+            service.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD),
+                    false, observer);
+            return observer;
+        }
+
+        void unregister() {
+            mService.getContentResolver().unregisterContentObserver(this);
+        }
+
+        private boolean shouldShowImeWithHardKeyboard() {
+            // Lazily initialize as needed.
+            if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
+                mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
+                        Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
+                        ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
+            }
+            switch (mShowImeWithHardKeyboard) {
+                case ShowImeWithHardKeyboardType.TRUE:
+                    return true;
+                case ShowImeWithHardKeyboardType.FALSE:
+                    return false;
+                default:
+                    Log.e(TAG, "Unexpected mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard);
+                    return false;
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            final Uri showImeWithHardKeyboardUri =
+                    Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+            if (showImeWithHardKeyboardUri.equals(uri)) {
+                mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
+                        Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
+                        ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
+                mService.updateInputViewShown();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard  + "}";
+        }
+    }
+    private SettingsObserver mSettingsObserver;
+
+    /**
      * You can call this to customize the theme used by your IME's window.
      * This theme should typically be one that derives from
      * {@link android.R.style#Theme_InputMethod}, which is the default theme
@@ -682,6 +781,7 @@
         super.setTheme(mTheme);
         super.onCreate();
         mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
+        mSettingsObserver = SettingsObserver.createAndRegister(this);
         // If the previous IME has occupied non-empty inset in the screen, we need to decide whether
         // we continue to use the same size of the inset or update it
         mShouldClearInsetOfPreviousIme = (mImm.getInputMethodWindowVisibleHeight() > 0);
@@ -764,6 +864,10 @@
             mWindow.getWindow().setWindowAnimations(0);
             mWindow.dismiss();
         }
+        if (mSettingsObserver != null) {
+            mSettingsObserver.unregister();
+            mSettingsObserver = null;
+        }
     }
 
     /**
@@ -1140,21 +1244,32 @@
     public boolean isInputViewShown() {
         return mIsInputViewShown && mWindowVisible;
     }
-    
+
     /**
-     * Override this to control when the soft input area should be shown to
-     * the user.  The default implementation only shows the input view when
-     * there is no hard keyboard or the keyboard is hidden.  If you change what
-     * this returns, you will need to call {@link #updateInputViewShown()}
-     * yourself whenever the returned value may have changed to have it
-     * re-evaluated and applied.
+     * Override this to control when the soft input area should be shown to the user.  The default
+     * implementation returns {@code false} when there is no hard keyboard or the keyboard is hidden
+     * unless the user shows an intention to use software keyboard.  If you change what this
+     * returns, you will need to call {@link #updateInputViewShown()} yourself whenever the returned
+     * value may have changed to have it re-evaluated and applied.
+     *
+     * <p>When you override this method, it is recommended to call
+     * {@code super.onEvaluateInputViewShown()} and return {@code true} when {@code true} is
+     * returned.</p>
      */
+    @CallSuper
     public boolean onEvaluateInputViewShown() {
+        if (mSettingsObserver == null) {
+            Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
+            return false;
+        }
+        if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
+            return true;
+        }
         Configuration config = getResources().getConfiguration();
         return config.keyboard == Configuration.KEYBOARD_NOKEYS
                 || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
     }
-    
+
     /**
      * Controls the visibility of the candidates display area.  By default
      * it is hidden.
@@ -2483,5 +2598,6 @@
                 + " touchableInsets=" + mTmpInsets.touchableInsets
                 + " touchableRegion=" + mTmpInsets.touchableRegion);
         p.println(" mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
+        p.println(" mSettingsObserver=" + mSettingsObserver);
     }
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 515e9a2..c4f0847 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -17,13 +17,14 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Handler;
@@ -46,12 +47,12 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Protocol;
 
-import java.net.InetAddress;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.HashMap;
-
 import libcore.net.event.NetworkEventDispatcher;
 
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * Class that answers queries about the state of network connectivity. It also
  * notifies applications when network connectivity changes. Get an instance
@@ -686,6 +687,47 @@
     }
 
     /**
+     * Configures an always-on VPN connection through a specific application.
+     * This connection is automatically granted and persisted after a reboot.
+     *
+     * <p>The designated package should declare a {@link VpnService} in its
+     *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+     *    otherwise the call will fail.
+     *
+     * @param userId The identifier of the user to set an always-on VPN for.
+     * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+     *                   to remove an existing always-on VPN configuration.
+
+     * @return {@code true} if the package is set as always-on VPN controller;
+     *         {@code false} otherwise.
+     * @hide
+     */
+    public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage) {
+        try {
+            return mService.setAlwaysOnVpnPackage(userId, vpnPackage);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the package name of the currently set always-on VPN application.
+     * If there is no always-on VPN set, or the VPN is provided by the system instead
+     * of by an app, {@code null} will be returned.
+     *
+     * @return Package name of VPN controller responsible for always-on VPN,
+     *         or {@code null} if none is set.
+     * @hide
+     */
+    public String getAlwaysOnVpnPackageForUser(int userId) {
+        try {
+            return mService.getAlwaysOnVpnPackage(userId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns details about the currently active default data network
      * for a given uid.  This is for internal use only to avoid spying
      * other apps.
@@ -898,6 +940,24 @@
     }
 
     /**
+     * Gets the URL that should be used for resolving whether a captive portal is present.
+     * 1. This URL should respond with a 204 response to a GET request to indicate no captive
+     *    portal is present.
+     * 2. This URL must be HTTP as redirect responses are used to find captive portal
+     *    sign-in pages. Captive portals cannot respond to HTTPS requests with redirects.
+     *
+     * @hide
+     */
+    @SystemApi
+    public String getCaptivePortalServerUrl() {
+        try {
+            return mService.getCaptivePortalServerUrl();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Tells the underlying networking system that the caller wants to
      * begin using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
@@ -1611,7 +1671,7 @@
             // Have a provisioning app - must only let system apps (which check this app)
             // turn on tethering
             context.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
+                    android.Manifest.permission.TETHER_PRIVILEGED, "ConnectivityService");
         } else {
             int uid = Binder.getCallingUid();
             Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d4dd669..569468e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -117,6 +117,8 @@
     VpnInfo[] getAllVpnInfo();
 
     boolean updateLockdownVpn();
+    boolean setAlwaysOnVpnPackage(int userId, String packageName);
+    String getAlwaysOnVpnPackage(int userId);
 
     int checkMobileProvisioning(int suggestedTimeOutMs);
 
@@ -165,4 +167,6 @@
             in IBinder binder, String srcAddr, int srcPort, String dstAddr);
 
     void stopKeepalive(in Network network, int slot);
+
+    String getCaptivePortalServerUrl();
 }
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index ec76b8a..e555fa4 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -33,6 +33,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -90,13 +91,18 @@
      * @return the list of scorers, or the empty list if there are no valid scorers.
      */
     public static Collection<NetworkScorerAppData> getAllValidScorers(Context context) {
-        List<NetworkScorerAppData> scorers = new ArrayList<>();
+        // Network scorer apps can only run as the primary user so exit early if we're not the
+        // primary user.
+        if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
+            return Collections.emptyList();
+        }
 
+        List<NetworkScorerAppData> scorers = new ArrayList<>();
         PackageManager pm = context.getPackageManager();
         // Only apps installed under the primary user of the device can be scorers.
         // TODO: http://b/23422763
         List<ResolveInfo> receivers =
-                pm.queryBroadcastReceivers(SCORE_INTENT, 0 /* flags */, UserHandle.USER_SYSTEM);
+                pm.queryBroadcastReceiversAsUser(SCORE_INTENT, 0 /* flags */, UserHandle.USER_SYSTEM);
         for (ResolveInfo receiver : receivers) {
             // This field is a misnomer, see android.content.pm.ResolveInfo#activityInfo
             final ActivityInfo receiverInfo = receiver.activityInfo;
@@ -105,8 +111,9 @@
                 continue;
             }
             if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) {
-                // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which means
-                // anyone could trigger network scoring and flood the framework with score requests.
+                // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which
+                // means anyone could trigger network scoring and flood the framework with score
+                // requests.
                 continue;
             }
             if (pm.checkPermission(permission.SCORE_NETWORKS, receiverInfo.packageName) !=
@@ -128,8 +135,8 @@
                 }
             }
 
-            // NOTE: loadLabel will attempt to load the receiver's label and fall back to the app
-            // label if none is present.
+            // NOTE: loadLabel will attempt to load the receiver's label and fall back to the
+            // app label if none is present.
             scorers.add(new NetworkScorerAppData(receiverInfo.packageName,
                     receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm),
                     configurationActivityClassName));
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index fdd34f5..e58744b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -130,6 +130,9 @@
         /** The dirty dalvik pages that have been swapped out. */
         /** @hide We may want to expose this, eventually. */
         public int dalvikSwappedOut;
+        /** The dirty dalvik pages that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        public int dalvikSwappedOutPss;
 
         /** The proportional set size for the native heap. */
         public int nativePss;
@@ -149,6 +152,9 @@
         /** The dirty native pages that have been swapped out. */
         /** @hide We may want to expose this, eventually. */
         public int nativeSwappedOut;
+        /** The dirty native pages that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        public int nativeSwappedOutPss;
 
         /** The proportional set size for everything else. */
         public int otherPss;
@@ -168,6 +174,13 @@
         /** The dirty pages used by anyting else that have been swapped out. */
         /** @hide We may want to expose this, eventually. */
         public int otherSwappedOut;
+        /** The dirty pages used by anyting else that have been swapped out, proportional. */
+        /** @hide We may want to expose this, eventually. */
+        public int otherSwappedOutPss;
+
+        /** Whether the kernel reports proportional swap usage */
+        /** @hide */
+        public boolean hasSwappedOutPss;
 
         /** @hide */
         public static final int HEAP_UNKNOWN = 0;
@@ -235,7 +248,7 @@
         public static final int NUM_DVK_STATS = 8;
 
         /** @hide */
-        public static final int NUM_CATEGORIES = 7;
+        public static final int NUM_CATEGORIES = 8;
 
         /** @hide */
         public static final int offsetPss = 0;
@@ -251,6 +264,8 @@
         public static final int offsetSharedClean = 5;
         /** @hide */
         public static final int offsetSwappedOut = 6;
+        /** @hide */
+        public static final int offsetSwappedOutPss = 7;
 
         private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
 
@@ -261,7 +276,7 @@
          * Return total PSS memory usage in kB.
          */
         public int getTotalPss() {
-            return dalvikPss + nativePss + otherPss;
+            return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
         }
 
         /**
@@ -274,7 +289,8 @@
         }
 
         /**
-         * Return total PSS memory usage in kB.
+         * Return total PSS memory usage in kB mapping a file of one of the following extension:
+         * .so, .jar, .apk, .ttf, .dex, .odex, .oat, .art .
          */
         public int getTotalSwappablePss() {
             return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
@@ -316,6 +332,14 @@
             return dalvikSwappedOut + nativeSwappedOut + otherSwappedOut;
         }
 
+        /**
+         * Return total swapped out memory in kB, proportional.
+         * @hide
+         */
+        public int getTotalSwappedOutPss() {
+            return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss;
+        }
+
         /** @hide */
         public int getOtherPss(int which) {
             return otherStats[which*NUM_CATEGORIES + offsetPss];
@@ -359,6 +383,11 @@
         }
 
         /** @hide */
+        public int getOtherSwappedOutPss(int which) {
+            return otherStats[which*NUM_CATEGORIES + offsetSwappedOutPss];
+        }
+
+        /** @hide */
         public static String getOtherLabel(int which) {
             switch (which) {
                 case OTHER_DALVIK_OTHER: return "Dalvik Other";
@@ -632,12 +661,24 @@
          *    know if the Swap memory is shared or private, so we don't know
          *    what to blame on the application and what on the system.
          *    For now, just lump all the Swap in one place.
+         *    For kernels reporting SwapPss {@link #getSummaryTotalSwapPss()}
+         *    will report the application proportional Swap.
          * @hide
          */
         public int getSummaryTotalSwap() {
             return getTotalSwappedOut();
         }
 
+        /**
+         * Total proportional Swap in KB.
+         * Notes:
+         *  * Always 0 if {@link #hasSwappedOutPss} is false.
+         * @hide
+         */
+        public int getSummaryTotalSwapPss() {
+            return getTotalSwappedOutPss();
+        }
+
         public int describeContents() {
             return 0;
         }
@@ -664,6 +705,8 @@
             dest.writeInt(otherPrivateClean);
             dest.writeInt(otherSharedClean);
             dest.writeInt(otherSwappedOut);
+            dest.writeInt(hasSwappedOutPss ? 1 : 0);
+            dest.writeInt(otherSwappedOutPss);
             dest.writeIntArray(otherStats);
         }
 
@@ -689,6 +732,8 @@
             otherPrivateClean = source.readInt();
             otherSharedClean = source.readInt();
             otherSwappedOut = source.readInt();
+            hasSwappedOutPss = source.readInt() != 0;
+            otherSwappedOutPss = source.readInt();
             otherStats = source.createIntArray();
         }
 
@@ -1563,11 +1608,12 @@
 
     /**
      * Retrieves the PSS memory used by the process as given by the
-     * smaps.  Optionally supply a long array of 1 entry to also
-     * receive the uss of the process, and another array to also
-     * retrieve the separate memtrack size.  @hide
+     * smaps.  Optionally supply a long array of 2 entries to also
+     * receive the Uss and SwapPss of the process, and another array to also
+     * retrieve the separate memtrack size.
+     * @hide
      */
-    public static native long getPss(int pid, long[] outUss, long[] outMemtrack);
+    public static native long getPss(int pid, long[] outUssSwapPss, long[] outMemtrack);
 
     /** @hide */
     public static final int MEMINFO_TOTAL = 0;
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 7a1a6a2..838279b 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.os.IMaintenanceActivityListener;
 import android.os.UserHandle;
 
 /** @hide */
@@ -37,4 +38,6 @@
     void exitIdle(String reason);
     void downloadServiceActive(IBinder token);
     void downloadServiceInactive();
+    boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener);
+    void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener);
 }
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/core/java/android/os/IMaintenanceActivityListener.aidl
similarity index 74%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to core/java/android/os/IMaintenanceActivityListener.aidl
index 805fe2c..6a2581f 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/core/java/android/os/IMaintenanceActivityListener.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2015, The Android Open Source Project
+ * 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.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.os;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+/** @hide */
+oneway interface IMaintenanceActivityListener {
+    void onMaintenanceActivityChanged(boolean active);
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 9ee6228..bc2566b 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -18,6 +18,7 @@
 package android.os;
 
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.content.pm.UserInfo;
 import android.content.RestrictionEntry;
 import android.graphics.Bitmap;
@@ -35,7 +36,7 @@
 
     UserInfo createUser(in String name, int flags);
     UserInfo createProfileForUser(in String name, int flags, int userHandle);
-    UserInfo createRestrictedProfile(String name, int parentUserId);
+    UserInfo createRestrictedProfile(String name, int parentUserHandle);
     void setUserEnabled(int userHandle);
     boolean removeUser(int userHandle);
     void setUserName(int userHandle, String name);
@@ -44,21 +45,21 @@
     UserInfo getPrimaryUser();
     List<UserInfo> getUsers(boolean excludeDying);
     List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
-    boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
+    boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
     UserInfo getProfileParent(int userHandle);
-    boolean isSameProfileGroup(int userId, int otherUserId);
+    boolean isSameProfileGroup(int userHandle, int otherUserHandle);
     UserInfo getUserInfo(int userHandle);
-    String getUserAccount(int userId);
-    void setUserAccount(int userId, String accountName);
+    String getUserAccount(int userHandle);
+    void setUserAccount(int userHandle, String accountName);
     long getUserCreationTime(int userHandle);
     boolean isRestricted();
-    boolean canHaveRestrictedProfile(int userId);
+    boolean canHaveRestrictedProfile(int userHandle);
     int getUserSerialNumber(int userHandle);
     int getUserHandle(int userSerialNumber);
     Bundle getUserRestrictions(int userHandle);
     boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
     boolean hasUserRestriction(in String restrictionKey, int userHandle);
-    void setUserRestriction(String key, boolean value, int userId);
+    void setUserRestriction(String key, boolean value, int userHandle);
     void setApplicationRestrictions(in String packageName, in Bundle restrictions,
             int userHandle);
     Bundle getApplicationRestrictions(in String packageName);
@@ -68,4 +69,11 @@
     boolean markGuestForDeletion(int userHandle);
     void setQuietModeEnabled(int userHandle, boolean enableQuietMode);
     boolean isQuietModeEnabled(int userHandle);
+    void setSeedAccountData(int userHandle, in String accountName,
+            in String accountType, in PersistableBundle accountOptions, boolean persist);
+    String getSeedAccountName();
+    String getSeedAccountType();
+    PersistableBundle getSeedAccountOptions();
+    void clearSeedAccountData();
+    boolean someUserHasSeedAccount(in String accountName, in String accountType);
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 126824f..4159d89 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -980,6 +980,14 @@
             = "android.os.action.POWER_SAVE_MODE_CHANGED";
 
     /**
+     * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
+     * @hide
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL
+            = "android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL";
+
+    /**
      * Intent that is broadcast when the state of {@link #isDeviceIdleMode()} changes.
      * This broadcast is only sent to registered receivers.
      */
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 13b8b66..2ba4aa4 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -112,8 +112,10 @@
 
     /**
      * Return a list of all currently running services.
+     * @return an array of all currently running services, or <code>null</code> in
+     * case of an exception
      */
-    public static String[] listServices() throws RemoteException {
+    public static String[] listServices() {
         try {
             return getIServiceManager().listServices();
         } catch (RemoteException e) {
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index f946ca7..bcc1213 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -16,7 +16,10 @@
 
 package android.os;
 
+import android.annotation.AppIdInt;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 
 import java.io.PrintWriter;
 
@@ -30,13 +33,13 @@
     public static final int PER_USER_RANGE = 100000;
 
     /** @hide A user id to indicate all users on the device */
-    public static final int USER_ALL = -1;
+    public static final @UserIdInt int USER_ALL = -1;
 
     /** @hide A user handle to indicate all users on the device */
     public static final UserHandle ALL = new UserHandle(USER_ALL);
 
     /** @hide A user id to indicate the currently active user */
-    public static final int USER_CURRENT = -2;
+    public static final @UserIdInt int USER_CURRENT = -2;
 
     /** @hide A user handle to indicate the current user of the device */
     public static final UserHandle CURRENT = new UserHandle(USER_CURRENT);
@@ -44,7 +47,7 @@
     /** @hide A user id to indicate that we would like to send to the current
      *  user, but if this is calling from a user process then we will send it
      *  to the caller's user instead of failing with a security exception */
-    public static final int USER_CURRENT_OR_SELF = -3;
+    public static final @UserIdInt int USER_CURRENT_OR_SELF = -3;
 
     /** @hide A user handle to indicate that we would like to send to the current
      *  user, but if this is calling from a user process then we will send it
@@ -52,14 +55,14 @@
     public static final UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF);
 
     /** @hide An undefined user id */
-    public static final int USER_NULL = -10000;
+    public static final @UserIdInt int USER_NULL = -10000;
 
     /**
      * @hide A user id constant to indicate the "owner" user of the device
      * @deprecated Consider using either {@link UserHandle#USER_SYSTEM} constant or
      * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
      */
-    public static final int USER_OWNER = 0;
+    public static final @UserIdInt int USER_OWNER = 0;
 
     /**
      * @hide A user handle to indicate the primary/owner user of the device
@@ -69,7 +72,7 @@
     public static final UserHandle OWNER = new UserHandle(USER_OWNER);
 
     /** @hide A user id constant to indicate the "system" user of the device */
-    public static final int USER_SYSTEM = 0;
+    public static final @UserIdInt int USER_SYSTEM = 0;
 
     /** @hide A user handle to indicate the "system" user of the device */
     public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
@@ -127,7 +130,7 @@
      * Returns the user id for a given uid.
      * @hide
      */
-    public static int getUserId(int uid) {
+    public static @UserIdInt int getUserId(int uid) {
         if (MU_ENABLED) {
             return uid / PER_USER_RANGE;
         } else {
@@ -136,12 +139,12 @@
     }
 
     /** @hide */
-    public static int getCallingUserId() {
+    public static @UserIdInt int getCallingUserId() {
         return getUserId(Binder.getCallingUid());
     }
 
     /** @hide */
-    public static UserHandle of(int userId) {
+    public static UserHandle of(@UserIdInt int userId) {
         return userId == USER_SYSTEM ? SYSTEM : new UserHandle(userId);
     }
 
@@ -149,7 +152,7 @@
      * Returns the uid that is composed from the userId and the appId.
      * @hide
      */
-    public static int getUid(int userId, int appId) {
+    public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
         if (MU_ENABLED) {
             return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
         } else {
@@ -161,7 +164,8 @@
      * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
      * @hide
      */
-    public static int getAppId(int uid) {
+    @TestApi
+    public static @AppIdInt int getAppId(int uid) {
         return uid % PER_USER_RANGE;
     }
 
@@ -169,7 +173,7 @@
      * Returns the gid shared between all apps with this userId.
      * @hide
      */
-    public static int getUserGid(int userId) {
+    public static int getUserGid(@UserIdInt int userId) {
         return getUid(userId, Process.SHARED_USER_GID);
     }
 
@@ -186,7 +190,7 @@
      * Returns the app id for a given shared app gid. Returns -1 if the ID is invalid.
      * @hide
      */
-    public static int getAppIdFromSharedAppGid(int gid) {
+    public static @AppIdInt int getAppIdFromSharedAppGid(int gid) {
         final int appId = getAppId(gid) + Process.FIRST_APPLICATION_UID
                 - Process.FIRST_SHARED_APPLICATION_GID;
         if (appId < 0 || appId >= Process.FIRST_SHARED_APPLICATION_GID) {
@@ -257,7 +261,7 @@
     }
 
     /** @hide */
-    public static int parseUserArg(String arg) {
+    public static @UserIdInt int parseUserArg(String arg) {
         int userId;
         if ("all".equals(arg)) {
             userId = UserHandle.USER_ALL;
@@ -279,7 +283,7 @@
      * @hide
      */
     @SystemApi
-    public static int myUserId() {
+    public static @UserIdInt int myUserId() {
         return getUserId(Process.myUid());
     }
 
@@ -315,7 +319,7 @@
      * @hide
      */
     @SystemApi
-    public int getIdentifier() {
+    public @UserIdInt int getIdentifier() {
         return mHandle;
     }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 037916a..f01f597 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -20,11 +20,13 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -517,6 +519,16 @@
     public static final String DISALLOW_CAMERA = "no_camera";
 
     /**
+     * Specifies if a user is not allowed to use cellular data when roaming. This can only be set by
+     * device owners. The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_DATA_ROAMING = "no_data_roaming";
+
+    /**
      * Allows apps in the parent profile to handle web links from the managed profile.
      *
      * This user restriction has an effect only in a managed profile.
@@ -556,6 +568,37 @@
      */
     public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
 
+    private static final String ACTION_CREATE_USER = "android.os.action.CREATE_USER";
+
+    /**
+     * Extra containing a name for the user being created. Optional parameter passed to
+     * ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_NAME = "android.os.extra.USER_NAME";
+
+    /**
+     * Extra containing account name for the user being created. Optional parameter passed to
+     * ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACCOUNT_NAME = "android.os.extra.USER_ACCOUNT_NAME";
+
+    /**
+     * Extra containing account type for the user being created. Optional parameter passed to
+     * ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACCOUNT_TYPE = "android.os.extra.USER_ACCOUNT_TYPE";
+
+    /**
+     * Extra containing account-specific data for the user being created. Optional parameter passed
+     * to ACTION_CREATE_USER activity.
+     * @hide
+     */
+    public static final String EXTRA_USER_ACCOUNT_OPTIONS
+            = "android.os.extra.USER_ACCOUNT_OPTIONS";
+
     /** @hide */
     public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
     /** @hide */
@@ -601,7 +644,7 @@
      * @return the user handle of this process.
      * @hide
      */
-    public int getUserHandle() {
+    public @UserIdInt int getUserHandle() {
         return UserHandle.myUserId();
     }
 
@@ -671,7 +714,7 @@
      * Returns whether the provided user is an admin user. There can be more than one admin
      * user.
      */
-    public boolean isUserAdmin(int userId) {
+    public boolean isUserAdmin(@UserIdInt int userId) {
         UserInfo user = getUserInfo(userId);
         return user != null && user.isAdmin();
     }
@@ -695,7 +738,7 @@
      * Checks if specified user can have restricted profile.
      * @hide
      */
-    public boolean canHaveRestrictedProfile(int userId) {
+    public boolean canHaveRestrictedProfile(@UserIdInt int userId) {
         try {
             return mService.canHaveRestrictedProfile(userId);
         } catch (RemoteException re) {
@@ -741,7 +784,7 @@
      * Returns whether the specified user is ephemeral.
      * @hide
      */
-    public boolean isUserEphemeral(int userId) {
+    public boolean isUserEphemeral(@UserIdInt int userId) {
         final UserInfo user = getUserInfo(userId);
         return user != null && user.isEphemeral();
     }
@@ -755,9 +798,13 @@
      * @param user The user to retrieve the running state for.
      */
     public boolean isUserRunning(UserHandle user) {
+        return isUserRunning(user.getIdentifier());
+    }
+
+    /** {@hide} */
+    public boolean isUserRunning(int userId) {
         try {
-            return ActivityManagerNative.getDefault().isUserRunning(
-                    user.getIdentifier(), 0);
+            return ActivityManagerNative.getDefault().isUserRunning(userId, 0);
         } catch (RemoteException e) {
             return false;
         }
@@ -861,7 +908,7 @@
     }
 
     /** {@hide} */
-    public boolean isUserUnlocked(int userId) {
+    public boolean isUserUnlocked(@UserIdInt int userId) {
         // TODO: eventually pivot this back to look at ActivityManager state,
         // but there is race where we can start a non-encryption-aware launcher
         // before that lifecycle has entered the running unlocked state.
@@ -875,7 +922,7 @@
      * @return the UserInfo object for a specific user.
      * @hide
      */
-    public UserInfo getUserInfo(int userHandle) {
+    public UserInfo getUserInfo(@UserIdInt int userHandle) {
         try {
             return mService.getUserInfo(userHandle);
         } catch (RemoteException re) {
@@ -1093,7 +1140,7 @@
      * @return the UserInfo object for the created user, or null if the user could not be created.
      * @hide
      */
-    public UserInfo createProfileForUser(String name, int flags, int userHandle) {
+    public UserInfo createProfileForUser(String name, int flags, @UserIdInt int userHandle) {
         try {
             return mService.createProfileForUser(name, flags, userHandle);
         } catch (RemoteException re) {
@@ -1127,13 +1174,144 @@
     }
 
     /**
+     * Returns an intent to create a user for the provided name and email address. The name
+     * and email address will be used when the setup process for the new user is started.
+     * If this device does not support multiple users, null is returned.
+     * <p/>
+     * The intent should be launched using startActivityForResult and the return result will
+     * indicate if the user consented to adding a new user and if the operation succeeded.
+     * <p/>
+     * The new user is created but not initialized. After switching into the user for the first
+     * time, the preferred user name and account information are used by the setup process for that
+     * user.
+     *
+     * @param userName Optional name to assign to the user.
+     * @param accountName Optional email address that will be used by the setup wizard to initialize
+     *                    the user.
+     * @param accountType Optional account type for the account to be created. This is required
+     *                    if the account name is specified.
+     * @param accountOptions Optional bundle of data to be passed in during account creation in the
+     *                       new user via {@link AccountManager#addAccount(String, String, String[],
+     *                       Bundle, android.app.Activity, android.accounts.AccountManagerCallback,
+     *                       Handler)}.
+     * @return An Intent that can be launched from an Activity or null if creating users is not
+     *         supported on this device.
+     */
+    public static Intent createUserCreationIntent(@Nullable String userName,
+            @Nullable String accountName,
+            @Nullable String accountType, @Nullable PersistableBundle accountOptions) {
+        if (!supportsMultipleUsers() || getMaxSupportedUsers() < 2) {
+            return null;
+        }
+        Intent intent = new Intent(ACTION_CREATE_USER);
+        if (userName != null) {
+            intent.putExtra(EXTRA_USER_NAME, userName);
+        }
+        if (accountName != null && accountType == null) {
+            throw new IllegalArgumentException("accountType must be specified if accountName is "
+                    + "specified");
+        }
+        if (accountName != null) {
+            intent.putExtra(EXTRA_USER_ACCOUNT_NAME, accountName);
+        }
+        if (accountType != null) {
+            intent.putExtra(EXTRA_USER_ACCOUNT_TYPE, accountType);
+        }
+        if (accountOptions != null) {
+            intent.putExtra(EXTRA_USER_ACCOUNT_OPTIONS, accountOptions);
+        }
+        return intent;
+    }
+
+    /**
+     * @hide
+     *
+     * Returns the preferred account name for user creation. Requires MANAGE_USERS permission.
+     */
+    @SystemApi
+    public String getSeedAccountName() {
+        try {
+            return mService.getSeedAccountName();
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get the seed account name", re);
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Returns the preferred account type for user creation. Requires MANAGE_USERS permission.
+     */
+    @SystemApi
+    public String getSeedAccountType() {
+        try {
+            return mService.getSeedAccountType();
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get the seed account type", re);
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Returns the preferred account's options bundle for user creation. Requires MANAGE_USERS
+     * permission.
+     * @return Any options set by the requestor that created the user.
+     */
+    @SystemApi
+    public PersistableBundle getSeedAccountOptions() {
+        try {
+            return mService.getSeedAccountOptions();
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not get the seed account options", re);
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     *
+     * Called by a system activity to set the seed account information of a user created
+     * through the user creation intent.
+     * @param userId
+     * @param accountName
+     * @param accountType
+     * @param accountOptions
+     * @see #createUserCreationIntent(String, String, String, PersistableBundle)
+     */
+    public void setSeedAccountData(int userId, String accountName, String accountType,
+            PersistableBundle accountOptions) {
+        try {
+            mService.setSeedAccountData(userId, accountName, accountType, accountOptions,
+                    /* persist= */ true);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not set the seed account data", re);
+        }
+    }
+
+    /**
+     * @hide
+     * Clears the seed information used to create this user. Requires MANAGE_USERS permission.
+     */
+    @SystemApi
+    public void clearSeedAccountData() {
+        try {
+            mService.clearSeedAccountData();
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not clear the seed account data", re);
+        }
+    }
+
+    /**
      * @hide
      * Marks the guest user for deletion to allow a new guest to be created before deleting
      * the current user who is a guest.
      * @param userHandle
      * @return
      */
-    public boolean markGuestForDeletion(int userHandle) {
+    public boolean markGuestForDeletion(@UserIdInt int userHandle) {
         try {
             return mService.markGuestForDeletion(userHandle);
         } catch (RemoteException re) {
@@ -1150,7 +1328,7 @@
      * @param userHandle the id of the profile to enable
      * @hide
      */
-    public void setUserEnabled(int userHandle) {
+    public void setUserEnabled(@UserIdInt int userHandle) {
         try {
             mService.setUserEnabled(userHandle);
         } catch (RemoteException e) {
@@ -1189,7 +1367,7 @@
             Manifest.permission.INTERACT_ACROSS_USERS_FULL,
             Manifest.permission.MANAGE_USERS
     })
-    public @Nullable String getUserAccount(int userHandle) {
+    public @Nullable String getUserAccount(@UserIdInt int userHandle) {
         try {
             return mService.getUserAccount(userHandle);
         } catch (RemoteException re) {
@@ -1206,7 +1384,7 @@
             Manifest.permission.INTERACT_ACROSS_USERS_FULL,
             Manifest.permission.MANAGE_USERS
     })
-    public void setUserAccount(int userHandle, @Nullable String accountName) {
+    public void setUserAccount(@UserIdInt int userHandle, @Nullable String accountName) {
         try {
             mService.setUserAccount(userHandle, accountName);
         } catch (RemoteException re) {
@@ -1259,7 +1437,7 @@
      * @return true if more managed profiles can be added, false if limit has been reached.
      * @hide
      */
-    public boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne) {
+    public boolean canAddMoreManagedProfiles(@UserIdInt int userId, boolean allowedToRemoveOne) {
         try {
             return mService.canAddMoreManagedProfiles(userId, allowedToRemoveOne);
         } catch (RemoteException re) {
@@ -1279,7 +1457,7 @@
      * @return the list of profiles.
      * @hide
      */
-    public List<UserInfo> getProfiles(int userHandle) {
+    public List<UserInfo> getProfiles(@UserIdInt int userHandle) {
         try {
             return mService.getProfiles(userHandle, false /* enabledOnly */);
         } catch (RemoteException re) {
@@ -1295,7 +1473,7 @@
      * @return true if the two user ids are in the same profile group.
      * @hide
      */
-    public boolean isSameProfileGroup(int userId, int otherUserId) {
+    public boolean isSameProfileGroup(@UserIdInt int userId, int otherUserId) {
         try {
             return mService.isSameProfileGroup(userId, otherUserId);
         } catch (RemoteException re) {
@@ -1314,7 +1492,7 @@
      * @return the list of profiles.
      * @hide
      */
-    public List<UserInfo> getEnabledProfiles(int userHandle) {
+    public List<UserInfo> getEnabledProfiles(@UserIdInt int userHandle) {
         try {
             return mService.getProfiles(userHandle, true /* enabledOnly */);
         } catch (RemoteException re) {
@@ -1352,7 +1530,7 @@
      *
      * @hide
      */
-    public int getCredentialOwnerProfile(int userHandle) {
+    public int getCredentialOwnerProfile(@UserIdInt int userHandle) {
         try {
             return mService.getCredentialOwnerProfile(userHandle);
         } catch (RemoteException re) {
@@ -1367,7 +1545,7 @@
      *
      * @hide
      */
-    public UserInfo getProfileParent(int userHandle) {
+    public UserInfo getProfileParent(@UserIdInt int userHandle) {
         try {
             return mService.getProfileParent(userHandle);
         } catch (RemoteException re) {
@@ -1383,7 +1561,7 @@
      * @param enableQuietMode Whether quiet mode should be enabled or disabled.
      * @hide
      */
-    public void setQuietModeEnabled(int userHandle, boolean enableQuietMode) {
+    public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) {
         try {
             mService.setQuietModeEnabled(userHandle, enableQuietMode);
         } catch (RemoteException e) {
@@ -1499,7 +1677,7 @@
      * @param userHandle the integer handle of the user, where 0 is the primary user.
      * @hide
      */
-    public boolean removeUser(int userHandle) {
+    public boolean removeUser(@UserIdInt int userHandle) {
         try {
             return mService.removeUser(userHandle);
         } catch (RemoteException re) {
@@ -1516,7 +1694,7 @@
      * @param name the new name for the user
      * @hide
      */
-    public void setUserName(int userHandle, String name) {
+    public void setUserName(@UserIdInt int userHandle, String name) {
         try {
             mService.setUserName(userHandle, name);
         } catch (RemoteException re) {
@@ -1530,7 +1708,7 @@
      * @param icon the bitmap to set as the photo.
      * @hide
      */
-    public void setUserIcon(int userHandle, Bitmap icon) {
+    public void setUserIcon(@UserIdInt int userHandle, Bitmap icon) {
         try {
             mService.setUserIcon(userHandle, icon);
         } catch (RemoteException re) {
@@ -1545,7 +1723,7 @@
      * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default.
      * @hide
      */
-    public Bitmap getUserIcon(int userHandle) {
+    public Bitmap getUserIcon(@UserIdInt int userHandle) {
         try {
             ParcelFileDescriptor fd = mService.getUserIcon(userHandle);
             if (fd != null) {
@@ -1611,7 +1789,7 @@
      * @return a serial number associated with that user, or -1 if the userHandle is not valid.
      * @hide
      */
-    public int getUserSerialNumber(int userHandle) {
+    public int getUserSerialNumber(@UserIdInt int userHandle) {
         try {
             return mService.getUserSerialNumber(userHandle);
         } catch (RemoteException re) {
@@ -1629,7 +1807,7 @@
      * is not valid.
      * @hide
      */
-    public int getUserHandle(int userSerialNumber) {
+    public @UserIdInt int getUserHandle(int userSerialNumber) {
         try {
             return mService.getUserHandle(userSerialNumber);
         } catch (RemoteException re) {
@@ -1741,4 +1919,21 @@
             return 0;
         }
     }
+
+    /**
+     * @hide
+     * Checks if any uninitialized user has the specific seed account name and type.
+     *
+     * @param mAccountName The account name to check for
+     * @param mAccountType The account type of the account to check for
+     * @return whether the seed account was found
+     */
+    public boolean someUserHasSeedAccount(String accountName, String accountType) {
+        try {
+            return mService.someUserHasSeedAccount(accountName, accountType);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not check seed accounts", re);
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index beda169..c8b942b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -882,7 +882,8 @@
                 }
                 packageName = packageNames[0];
             }
-            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, userId);
+            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
+                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
             if (uid <= 0) {
                 return new StorageVolume[0];
             }
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index c3625b8..469a4ea 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -98,5 +98,11 @@
     void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
     void setClient(IPrintSpoolerClient client);
     void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
-    void removeApprovedPrintService(in ComponentName serviceToRemove);
+
+    /**
+     * Remove all approved print services that are not in the given set.
+     *
+     * @param servicesToKeep The names of the services to keep
+     */
+    void pruneApprovedPrintServices(in List<ComponentName> servicesToKeep);
 }
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 83efe80..ff9c0df 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -20,16 +20,17 @@
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
 
 /**
  * This class represents the unique id of a printer.
  */
 public final class PrinterId implements Parcelable {
 
-    private final ComponentName mServiceName;
+    private final @NonNull ComponentName mServiceName;
 
-    private final String mLocalId;
+    private final @NonNull String mLocalId;
 
     /**
      * Creates a new instance.
@@ -39,14 +40,14 @@
      *
      * @hide
      */
-    public PrinterId(ComponentName serviceName, String localId) {
+    public PrinterId(@NonNull ComponentName serviceName, @NonNull String localId) {
         mServiceName = serviceName;
         mLocalId = localId;
     }
 
-    private PrinterId(Parcel parcel) {
-        mServiceName = parcel.readParcelable(null);
-        mLocalId = parcel.readString();
+    private PrinterId(@NonNull Parcel parcel) {
+        mServiceName = Preconditions.checkNotNull((ComponentName) parcel.readParcelable(null));
+        mLocalId = Preconditions.checkNotNull(parcel.readString());
     }
 
     /**
@@ -56,7 +57,7 @@
      *
      * @hide
      */
-    public ComponentName getServiceName() {
+    public @NonNull ComponentName getServiceName() {
         return mServiceName;
     }
 
@@ -93,14 +94,10 @@
             return false;
         }
         PrinterId other = (PrinterId) object;
-        if (mServiceName == null) {
-            if (other.mServiceName != null) {
-                return false;
-            }
-        } else if (!mServiceName.equals(other.mServiceName)) {
+        if (!mServiceName.equals(other.mServiceName)) {
             return false;
         }
-        if (!TextUtils.equals(mLocalId, other.mLocalId)) {
+        if (!mLocalId.equals(other.mLocalId)) {
             return false;
         }
         return true;
@@ -110,8 +107,7 @@
     public int hashCode() {
         final int prime = 31;
         int hashCode = 1;
-        hashCode = prime * hashCode + ((mServiceName != null)
-                ? mServiceName.hashCode() : 1);
+        hashCode = prime * hashCode + mServiceName.hashCode();
         hashCode = prime * hashCode + mLocalId.hashCode();
         return hashCode;
     }
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 20d91b2..0d2d9f4 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -33,6 +33,8 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.internal.util.Preconditions;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -43,6 +45,9 @@
  * major components, printer properties such as name, id, status,
  * description and printer capabilities which describe the various
  * print modes a printer supports such as media sizes, margins, etc.
+ * <p>
+ * Once {@link PrinterInfo.Builder#build() built} the objects are immutable.
+ * </p>
  */
 public final class PrinterInfo implements Parcelable {
 
@@ -62,60 +67,41 @@
     /** Printer status: the printer is not available. */
     public static final int STATUS_UNAVAILABLE = 3;
 
-    private PrinterId mId;
+    private final @NonNull PrinterId mId;
 
     /** Resource inside the printer's services's package to be used as an icon */
-    private int mIconResourceId;
+    private final int mIconResourceId;
 
     /** If a custom icon can be loaded for the printer */
-    private boolean mHasCustomPrinterIcon;
+    private final boolean mHasCustomPrinterIcon;
 
     /** The generation of the icon in the cache. */
-    private int mCustomPrinterIconGen;
+    private final int mCustomPrinterIconGen;
 
     /** Intent that launches the activity showing more information about the printer. */
-    private PendingIntent mInfoIntent;
+    private final @Nullable PendingIntent mInfoIntent;
 
-    private String mName;
+    private final @NonNull String mName;
 
-    private int mStatus;
+    private final @Status int mStatus;
 
-    private String mDescription;
+    private final @Nullable String mDescription;
 
-    private PrinterCapabilitiesInfo mCapabilities;
+    private final @Nullable PrinterCapabilitiesInfo mCapabilities;
 
-    private PrinterInfo() {
-        /* do nothing */
-    }
-
-    private PrinterInfo(PrinterInfo prototype) {
-        copyFrom(prototype);
-    }
-
-    /**
-     * @hide
-     */
-    public void copyFrom(PrinterInfo other) {
-        if (this == other) {
-            return;
-        }
-        mId = other.mId;
-        mName = other.mName;
-        mStatus = other.mStatus;
-        mDescription = other.mDescription;
-        if (other.mCapabilities != null) {
-            if (mCapabilities != null) {
-                mCapabilities.copyFrom(other.mCapabilities);
-            } else {
-                mCapabilities = new PrinterCapabilitiesInfo(other.mCapabilities);
-            }
-        } else {
-            mCapabilities = null;
-        }
-        mIconResourceId = other.mIconResourceId;
-        mHasCustomPrinterIcon = other.mHasCustomPrinterIcon;
-        mCustomPrinterIconGen = other.mCustomPrinterIconGen;
-        mInfoIntent = other.mInfoIntent;
+    private PrinterInfo(@NonNull PrinterId printerId, @NonNull String name, @Status int status,
+            int iconResourceId, boolean hasCustomPrinterIcon, String description,
+            PendingIntent infoIntent, PrinterCapabilitiesInfo capabilities,
+            int customPrinterIconGen) {
+        mId = printerId;
+        mName = name;
+        mStatus = status;
+        mIconResourceId = iconResourceId;
+        mHasCustomPrinterIcon = hasCustomPrinterIcon;
+        mDescription = description;
+        mInfoIntent = infoIntent;
+        mCapabilities = capabilities;
+        mCustomPrinterIconGen = customPrinterIconGen;
     }
 
     /**
@@ -180,7 +166,7 @@
      *
      * @return The printer name.
      */
-    public @Nullable String getName() {
+    public @NonNull String getName() {
         return mName;
     }
 
@@ -227,10 +213,51 @@
         return mCapabilities;
     }
 
+    /**
+     * Check if printerId is valid.
+     *
+     * @param printerId The printerId that might be valid
+     * @return The valid printerId
+     * @throws IllegalArgumentException if printerId is not valid.
+     */
+    private static @NonNull PrinterId checkPrinterId(PrinterId printerId) {
+        return Preconditions.checkNotNull(printerId, "printerId cannot be null.");
+    }
+
+    /**
+     * Check if status is valid.
+     *
+     * @param status The status that might be valid
+     * @return The valid status
+     * @throws IllegalArgumentException if status is not valid.
+     */
+    private static @Status int checkStatus(int status) {
+        if (!(status == STATUS_IDLE
+                || status == STATUS_BUSY
+                || status == STATUS_UNAVAILABLE)) {
+            throw new IllegalArgumentException("status is invalid.");
+        }
+
+        return status;
+    }
+
+    /**
+     * Check if name is valid.
+     *
+     * @param name The name that might be valid
+     * @return The valid name
+     * @throws IllegalArgumentException if name is not valid.
+     */
+    private static @NonNull String checkName(String name) {
+        return Preconditions.checkStringNotEmpty(name, "name cannot be empty.");
+    }
+
     private PrinterInfo(Parcel parcel) {
-        mId = parcel.readParcelable(null);
-        mName = parcel.readString();
-        mStatus = parcel.readInt();
+        // mName can be null due to unchecked set in Builder.setName and status can be invalid
+        // due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state
+        mId = checkPrinterId((PrinterId) parcel.readParcelable(null));
+        mName = checkName(parcel.readString());
+        mStatus = checkStatus(parcel.readInt());
         mDescription = parcel.readString();
         mCapabilities = parcel.readParcelable(null);
         mIconResourceId = parcel.readInt();
@@ -261,8 +288,8 @@
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((mId != null) ? mId.hashCode() : 0);
-        result = prime * result + ((mName != null) ? mName.hashCode() : 0);
+        result = prime * result + mId.hashCode();
+        result = prime * result + mName.hashCode();
         result = prime * result + mStatus;
         result = prime * result + ((mDescription != null) ? mDescription.hashCode() : 0);
         result = prime * result + ((mCapabilities != null) ? mCapabilities.hashCode() : 0);
@@ -282,15 +309,11 @@
      * @hide
      */
     public boolean equalsIgnoringStatus(PrinterInfo other) {
-        if (mId == null) {
-            if (other.mId != null) {
-                return false;
-            }
-        } else if (!mId.equals(other.mId)) {
+        if (!mId.equals(other.mId)) {
             return false;
         }
-        if (!TextUtils.equals(mName, other.mName)) {
-            return false;
+        if (!mName.equals(other.mName)) {
+           return false;
         }
         if (!TextUtils.equals(mDescription, other.mDescription)) {
             return false;
@@ -363,7 +386,15 @@
      * Builder for creating of a {@link PrinterInfo}.
      */
     public static final class Builder {
-        private final PrinterInfo mPrototype;
+        private @NonNull PrinterId mPrinterId;
+        private @NonNull String mName;
+        private @Status int mStatus;
+        private int mIconResourceId;
+        private boolean mHasCustomPrinterIcon;
+        private String mDescription;
+        private PendingIntent mInfoIntent;
+        private PrinterCapabilitiesInfo mCapabilities;
+        private int mCustomPrinterIconGen;
 
         /**
          * Constructor.
@@ -375,19 +406,9 @@
          * printer name is empty or the status is not a valid one.
          */
         public Builder(@NonNull PrinterId printerId, @NonNull String name, @Status int status) {
-            if (printerId == null) {
-                throw new IllegalArgumentException("printerId cannot be null.");
-            }
-            if (TextUtils.isEmpty(name)) {
-                throw new IllegalArgumentException("name cannot be empty.");
-            }
-            if (!isValidStatus(status)) {
-                throw new IllegalArgumentException("status is invalid.");
-            }
-            mPrototype = new PrinterInfo();
-            mPrototype.mId = printerId;
-            mPrototype.mName = name;
-            mPrototype.mStatus = status;
+            mPrinterId = checkPrinterId(printerId);
+            mName = checkName(name);
+            mStatus = checkStatus(status);
         }
 
         /**
@@ -396,8 +417,15 @@
          * @param other Other info from which to start building.
          */
         public Builder(@NonNull PrinterInfo other) {
-            mPrototype = new PrinterInfo();
-            mPrototype.copyFrom(other);
+            mPrinterId = other.mId;
+            mName = other.mName;
+            mStatus = other.mStatus;
+            mIconResourceId = other.mIconResourceId;
+            mHasCustomPrinterIcon = other.mHasCustomPrinterIcon;
+            mDescription = other.mDescription;
+            mInfoIntent = other.mInfoIntent;
+            mCapabilities = other.mCapabilities;
+            mCustomPrinterIconGen = other.mCustomPrinterIconGen;
         }
 
         /**
@@ -405,13 +433,12 @@
          *
          * @param status The status.
          * @return This builder.
-         *
          * @see PrinterInfo#STATUS_IDLE
          * @see PrinterInfo#STATUS_BUSY
          * @see PrinterInfo#STATUS_UNAVAILABLE
          */
-        public @Nullable Builder setStatus(@Status int status) {
-            mPrototype.mStatus = status;
+        public @NonNull Builder setStatus(@Status int status) {
+            mStatus = checkStatus(status);
             return this;
         }
 
@@ -424,7 +451,8 @@
          * @see PrinterInfo.Builder#setHasCustomPrinterIcon
          */
         public @NonNull Builder setIconResourceId(@DrawableRes int iconResourceId) {
-            mPrototype.mIconResourceId = iconResourceId;
+            mIconResourceId = Preconditions.checkArgumentNonnegative(iconResourceId,
+                    "iconResourceId can't be negative");
             return this;
         }
 
@@ -442,7 +470,7 @@
          * @return This builder.
          */
         public @NonNull Builder setHasCustomPrinterIcon() {
-            mPrototype.mHasCustomPrinterIcon = true;
+            mHasCustomPrinterIcon = true;
             return this;
         }
 
@@ -454,7 +482,7 @@
          * @return This builder.
          */
         public @NonNull Builder setName(@NonNull String name) {
-            mPrototype.mName = name;
+            mName = checkName(name);
             return this;
         }
 
@@ -466,7 +494,7 @@
          * @return This builder.
          */
         public @NonNull Builder setDescription(@NonNull String description) {
-            mPrototype.mDescription = description;
+            mDescription = description;
             return this;
         }
 
@@ -478,7 +506,7 @@
          * @return This builder.
          */
         public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) {
-            mPrototype.mInfoIntent = infoIntent;
+            mInfoIntent = infoIntent;
             return this;
         }
 
@@ -489,7 +517,7 @@
          * @return This builder.
          */
         public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) {
-            mPrototype.mCapabilities = capabilities;
+            mCapabilities = capabilities;
             return this;
         }
 
@@ -499,13 +527,9 @@
          * @return A new {@link PrinterInfo}.
          */
         public @NonNull PrinterInfo build() {
-            return mPrototype;
-        }
-
-        private boolean isValidStatus(int status) {
-            return (status == STATUS_IDLE
-                    || status == STATUS_BUSY
-                    || status == STATUS_UNAVAILABLE);
+            return new PrinterInfo(mPrinterId, mName, mStatus, mIconResourceId,
+                    mHasCustomPrinterIcon, mDescription, mInfoIntent, mCapabilities,
+                    mCustomPrinterIconGen);
         }
 
         /**
@@ -517,7 +541,7 @@
          * @hide
          */
         public @NonNull Builder incCustomPrinterIconGen() {
-            mPrototype.mCustomPrinterIconGen++;
+            mCustomPrinterIconGen++;
             return this;
         }
     }
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index d0037b7..3104492 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -30,6 +30,8 @@
 import android.print.PrinterId;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -346,6 +348,7 @@
      */
     public final PrinterId generatePrinterId(String localId) {
         throwIfNotCalledOnMainThread();
+        localId = Preconditions.checkNotNull(localId, "localId cannot be null");
         return new PrinterId(new ComponentName(getPackageName(),
                 getClass().getName()), localId);
     }
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index b33ef83..91e01f2 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.printservice;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -94,6 +95,16 @@
     }
 
     /**
+     * Return the component name for this print service.
+     *
+     * @return The component name for this print service.
+     */
+    public @NonNull ComponentName getComponentName() {
+        return new ComponentName(mResolveInfo.serviceInfo.packageName,
+                mResolveInfo.serviceInfo.name);
+    }
+
+    /**
      * Creates a new instance.
      *
      * @param resolveInfo The service resolve info.
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
new file mode 100644
index 0000000..2a9d4ae
--- /dev/null
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -0,0 +1,128 @@
+/*
+ * 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.provider;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Constants and methods to access blocked phone numbers for incoming calls and texts.
+ *
+ * TODO javadoc
+ * - Proper javadoc tagging.
+ * - Code sample?
+ * - Describe who can access it.
+ */
+public class BlockedNumberContract {
+    private BlockedNumberContract() {
+    }
+
+    /** The authority for the contacts provider */
+    public static final String AUTHORITY = "com.android.blockednumber";
+
+    /** A content:// style uri to the authority for the contacts provider */
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    /**
+     * TODO javadoc
+     *
+     * Constants to interact with the blocked phone number list.
+     */
+    public static class BlockedNumbers {
+        private BlockedNumbers() {
+        }
+
+        /**
+         * TODO javadoc
+         *
+         * Content URI for the blocked numbers.
+         *
+         * Supported operations
+         * blocked
+         * - query
+         * - delete
+         * - insert
+         *
+         * blocked/ID
+         * - query (selection is not supported)
+         * - delete (selection is not supported)
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+                "blocked");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} itself providing a directory of blocked phone
+         * numbers.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+
+        /**
+         * The MIME type of a blocked phone number under {@link #CONTENT_URI}.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+
+        /**
+         * Auto-generated ID field which monotonically increases.
+         * <p>TYPE: long</p>
+         */
+        public static final String COLUMN_ID = "_id";
+
+        /**
+         * Phone number to block.
+         * <p>Must be specified in {@code insert}.
+         * <p>TYPE: String</p>
+         */
+        public static final String COLUMN_ORIGINAL_NUMBER = "original_number";
+
+        /**
+         * Phone number to block.  The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
+         * by removing all formatting characters.
+         * <p>Must NOT be specified in {@code insert}.
+         * <p>TYPE: String</p>
+         */
+        public static final String COLUMN_STRIPPED_NUMBER = "stripped_number";
+
+        /**
+         * Phone number to block.  The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
+         * by removing all formatting characters.
+         * <p>Optional in {@code insert}.  When not specified, the system tries to generate it
+         * assuming the current country. (Which will still be null if the number is not valid.)
+         * <p>TYPE: String</p>
+         */
+        public static final String COLUMN_E164_NUMBER = "e164_number";
+    }
+
+    /** @hide */
+    public static final String METHOD_IS_BLOCKED = "is_blocked";
+
+    /** @hide */
+    public static final String RES_NUMBER_IS_BLOCKED = "blocked";
+
+    /**
+     * Returns whether a given number is in the blocked list.
+     *
+     * TODO This should probably catch IllegalArgumentException to guard against the case where
+     * the provider is encrypted or the user is not running.
+     * (See addEntryAndRemoveExpiredEntries() in
+     * http://ag/#/c/844426/3/core/java/android/provider/CallLog.java)
+     */
+    public static boolean isBlocked(Context context, String phoneNumber) {
+        final Bundle res = context.getContentResolver().call(AUTHORITY_URI,
+                METHOD_IS_BLOCKED, phoneNumber, null);
+        return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+    }
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 10432b5..b547432 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -409,6 +409,20 @@
                 "directories_enterprise");
 
         /**
+         * Access file provided by remote directory. It allows both personal and work remote
+         * directory, but not local and invisible diretory.
+         *
+         * It's supported only by a few specific places for referring to contact pictures in the
+         * remote directory. Contact picture URIs, e.g.
+         * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}, may contain this kind of URI.
+         *
+         * @hide
+         */
+        public static final Uri ENTERPRISE_FILE_URI = Uri.withAppendedPath(AUTHORITY_URI,
+                "directory_file_enterprise");
+
+
+        /**
          * The MIME-type of {@link #CONTENT_URI} providing a directory of
          * contact directories.
          */
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 084ff77..a401ac2 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -230,7 +230,6 @@
          * @see #FLAG_SUPPORTS_WRITE
          * @see #FLAG_SUPPORTS_DELETE
          * @see #FLAG_SUPPORTS_THUMBNAIL
-         * @see #FLAG_SUPPORTS_TYPED_DOCUMENT
          * @see #FLAG_DIR_PREFERS_GRID
          * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
          * @see #FLAG_VIRTUAL_DOCUMENT
@@ -349,15 +348,6 @@
         public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
 
         /**
-         * Flag indicating that a document can be converted to alternative types.
-         *
-         * @see #COLUMN_FLAGS
-         * @see DocumentsProvider#openTypedDocument(String, String, Bundle,
-         *      android.os.CancellationSignal)
-         */
-        public static final int FLAG_SUPPORTS_TYPED_DOCUMENT = 1 << 9;
-
-        /**
          * Flag indicating that a document is virtual, and doesn't have byte
          * representation in the MIME type specified as {@link #COLUMN_MIME_TYPE}.
          *
@@ -366,7 +356,7 @@
          * @see DocumentsProvider#openTypedDocument(String, String, Bundle,
          *      android.os.CancellationSignal)
          */
-        public static final int FLAG_VIRTUAL_DOCUMENT = 1 << 10;
+        public static final int FLAG_VIRTUAL_DOCUMENT = 1 << 9;
 
         /**
          * Flag indicating that a document is an archive, and it's contents can be
@@ -378,7 +368,7 @@
          * @see #COLUMN_FLAGS
          * @see DocumentsProvider#queryChildDocuments(String, String[], String)
          */
-        public static final int FLAG_ARCHIVE = 1 << 11;
+        public static final int FLAG_ARCHIVE = 1 << 10;
 
         /**
          * Flag indicating that document titles should be hidden when viewing
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index e25ba35..94b4157 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -517,13 +517,12 @@
      *            provider.
      * @param signal used by the caller to signal if the request should be
      *            cancelled. May be null.
-     * @see Document#FLAG_SUPPORTS_TYPED_DOCUMENT
      */
     @SuppressWarnings("unused")
     public AssetFileDescriptor openTypedDocument(
             String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
             throws FileNotFoundException {
-        throw new UnsupportedOperationException("Typed documents not supported");
+        throw new FileNotFoundException("The requested MIME type is not supported.");
     }
 
     /**
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index b2d9b934..8472f78 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -609,7 +609,7 @@
          * This download has successfully completed.
          * Warning: there might be other status values that indicate success
          * in the future.
-         * Use isSucccess() to capture the entire category.
+         * Use isStatusSuccess() to capture the entire category.
          */
         public static final int STATUS_SUCCESS = 200;
 
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 48b3c1a..89ac27c9a 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -384,8 +384,14 @@
 
     public interface MediaColumns extends BaseColumns {
         /**
-         * The data stream for the file
-         * <P>Type: DATA STREAM</P>
+         * Path to the file on disk.
+         * <p>
+         * Note that apps may not have filesystem permissions to directly access
+         * this path. Instead of trying to open this path directly, apps should
+         * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+         * access.
+         * <p>
+         * Type: TEXT
          */
         public static final String DATA = "_data";
 
@@ -1149,8 +1155,15 @@
             public static final String DEFAULT_SORT_ORDER = "image_id ASC";
 
             /**
-             * The data stream for the thumbnail
-             * <P>Type: DATA STREAM</P>
+             * Path to the thumbnail file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             * <p>
+             * Type: TEXT
              */
             public static final String DATA = "_data";
 
@@ -1596,8 +1609,15 @@
             public static final String NAME = "name";
 
             /**
-             * The data stream for the playlist file
-             * <P>Type: DATA STREAM</P>
+             * Path to the playlist file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             * <p>
+             * Type: TEXT
              */
             public static final String DATA = "_data";
 
@@ -2192,8 +2212,15 @@
             public static final String DEFAULT_SORT_ORDER = "video_id ASC";
 
             /**
-             * The data stream for the thumbnail
-             * <P>Type: DATA STREAM</P>
+             * Path to the thumbnail file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             * <p>
+             * Type: TEXT
              */
             public static final String DATA = "_data";
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
old mode 100644
new mode 100755
index b883f9c..3042aa9
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -19,6 +19,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.Application;
@@ -640,7 +641,7 @@
      * provided by the platform for applications to operate correctly in the various power
      * saving mode.  This is only for unusual applications that need to deeply control their own
      * execution, at the potential expense of the user's battery life.  Note that these applications
-     * greatly run the risk of showing to the user has how power consumers on their device.</p>
+     * greatly run the risk of showing to the user as high power consumers on their device.</p>
      * <p>
      * Input: The Intent's data URI must specify the application package name
      * to be shown, with the "package" scheme.  That is "package:com.my.app".
@@ -1138,6 +1139,19 @@
     /** @hide */ public static final String EXTRA_APP_UID = "app_uid";
     /** @hide */ public static final String EXTRA_APP_PACKAGE = "app_package";
 
+    /**
+     * Activity Action: Show a dialog with disabled by policy message.
+     * <p> If an user action is disabled by policy, this dialog can be triggered to let
+     * the user know about this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS
+            = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
+
     // End of Intent actions for Settings
 
     /**
@@ -2592,6 +2606,15 @@
         private static final Validator MICROPHONE_MUTE_VALIDATOR = sBooleanValidator;
 
         /**
+         * Master mono (int 1 = mono, 0 = normal).
+         *
+         * @hide
+         */
+        public static final String MASTER_MONO = "master_mono";
+
+        private static final Validator MASTER_MONO_VALIDATOR = sBooleanValidator;
+
+        /**
          * Whether the notifications should use the ring volume (value of 1) or
          * a separate notification volume (value of 0). In most cases, users
          * will have this enabled so the notification and ringer volumes will be
@@ -3286,7 +3309,8 @@
             VIBRATE_WHEN_RINGING,
             RINGTONE,
             LOCK_TO_APP_ENABLED,
-            NOTIFICATION_SOUND
+            NOTIFICATION_SOUND,
+            ACCELEROMETER_ROTATION
         };
 
         /**
@@ -3357,6 +3381,7 @@
             PRIVATE_SETTINGS.add(VOLUME_MASTER);
             PRIVATE_SETTINGS.add(VOLUME_MASTER_MUTE);
             PRIVATE_SETTINGS.add(MICROPHONE_MUTE);
+            PRIVATE_SETTINGS.add(MASTER_MONO);
             PRIVATE_SETTINGS.add(NOTIFICATIONS_USE_RING_VOLUME);
             PRIVATE_SETTINGS.add(VIBRATE_IN_SILENT);
             PRIVATE_SETTINGS.add(MEDIA_BUTTON_RECEIVER);
@@ -3435,6 +3460,7 @@
             VALIDATORS.put(VIBRATE_INPUT_DEVICES, VIBRATE_INPUT_DEVICES_VALIDATOR);
             VALIDATORS.put(VOLUME_MASTER_MUTE, VOLUME_MASTER_MUTE_VALIDATOR);
             VALIDATORS.put(MICROPHONE_MUTE, MICROPHONE_MUTE_VALIDATOR);
+            VALIDATORS.put(MASTER_MONO, MASTER_MONO_VALIDATOR);
             VALIDATORS.put(NOTIFICATIONS_USE_RING_VOLUME, NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR);
             VALIDATORS.put(VIBRATE_IN_SILENT, VIBRATE_IN_SILENT_VALIDATOR);
             VALIDATORS.put(MEDIA_BUTTON_RECEIVER, MEDIA_BUTTON_RECEIVER_VALIDATOR);
@@ -4349,6 +4375,7 @@
          * The currently selected voice interaction service flattened ComponentName.
          * @hide
          */
+        @TestApi
         public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
 
         /**
@@ -4402,6 +4429,13 @@
         public static final String HTTP_PROXY = Global.HTTP_PROXY;
 
         /**
+         * Package designated as always-on VPN provider.
+         *
+         * @hide
+         */
+        public static final String ALWAYS_ON_VPN_APP = "always_on_vpn_app";
+
+        /**
          * Whether applications can be installed for this user via the system's
          * {@link Intent#ACTION_INSTALL_PACKAGE} mechanism.
          *
@@ -4972,19 +5006,22 @@
 
         /**
          * List of the enabled print services.
+         *
+         * N and beyond uses {@link #DISABLED_PRINT_SERVICES}. But this might be used in an upgrade
+         * from pre-N.
+         *
          * @hide
          */
         public static final String ENABLED_PRINT_SERVICES =
             "enabled_print_services";
 
         /**
-         * List of the system print services we enabled on first boot. On
-         * first boot we enable all system, i.e. bundled print services,
-         * once, so they work out-of-the-box.
+         * List of the disabled print services.
+         *
          * @hide
          */
-        public static final String ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES =
-            "enabled_on_first_boot_system_print_services";
+        public static final String DISABLED_PRINT_SERVICES =
+            "disabled_print_services";
 
         /**
          * Setting to always use the default text-to-speech settings regardless
@@ -5795,10 +5832,15 @@
             PARENTAL_CONTROL_ENABLED,
             PARENTAL_CONTROL_REDIRECT_URL,
             USB_MASS_STORAGE_ENABLED,                           // moved to global
+            ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+            ACCESSIBILITY_DISPLAY_DALTONIZER,
+            ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
+            ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
             ACCESSIBILITY_SCRIPT_INJECTION,
+            ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
             BACKUP_AUTO_RESTORE,
             ENABLED_ACCESSIBILITY_SERVICES,
             ENABLED_NOTIFICATION_LISTENERS,
@@ -5808,6 +5850,7 @@
             ACCESSIBILITY_ENABLED,
             ACCESSIBILITY_SPEAK_PASSWORD,
             ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+            ACCESSIBILITY_CAPTIONING_PRESET,
             ACCESSIBILITY_CAPTIONING_ENABLED,
             ACCESSIBILITY_CAPTIONING_LOCALE,
             ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
@@ -5816,6 +5859,7 @@
             ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
             ACCESSIBILITY_CAPTIONING_TYPEFACE,
             ACCESSIBILITY_CAPTIONING_FONT_SCALE,
+            ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
             TTS_USE_DEFAULTS,
             TTS_DEFAULT_RATE,
             TTS_DEFAULT_PITCH,
@@ -5824,6 +5868,7 @@
             TTS_DEFAULT_COUNTRY,
             TTS_ENABLED_PLUGINS,
             TTS_DEFAULT_LOCALE,
+            SHOW_IME_WITH_HARD_KEYBOARD,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,            // moved to global
             WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,               // moved to global
             WIFI_NUM_OPEN_NETWORKS_KEPT,                        // moved to global
@@ -5837,9 +5882,16 @@
             UI_NIGHT_MODE,
             SLEEP_TIMEOUT,
             DOUBLE_TAP_TO_WAKE,
+            WAKE_GESTURE_ENABLED,
+            LONG_PRESS_TIMEOUT,
             CAMERA_GESTURE_DISABLED,
             ACCESSIBILITY_AUTOCLICK_ENABLED,
-            ACCESSIBILITY_AUTOCLICK_DELAY
+            ACCESSIBILITY_AUTOCLICK_DELAY,
+            ACCESSIBILITY_LARGE_POINTER_ICON,
+            PREFERRED_TTY_MODE,
+            ENHANCED_VOICE_PRIVACY_ENABLED,
+            TTY_MODE_ENABLED,
+            INCALL_POWER_BUTTON_BEHAVIOR
         };
 
         /**
@@ -6374,6 +6426,13 @@
         public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
                 = "force_resizable_activities";
 
+        /**
+         * Whether to enable experimental freeform support for windows.
+         * @hide
+         */
+        public static final String DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT
+                = "enable_freeform_support";
+
        /**
         * Whether user has enabled development settings.
         */
@@ -7357,6 +7416,9 @@
                 BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_";
         /** {@hide} */
         public static final String
+                BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_";
+        /** {@hide} */
+        public static final String
                 BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_";
         /** {@hide} */
         public static final String
@@ -7404,10 +7466,12 @@
          * The following keys are supported:
          *
          * <pre>
-         * idle_duration        (long)
+         * idle_duration2       (long)
          * wallclock_threshold  (long)
          * parole_interval      (long)
          * parole_duration      (long)
+         *
+         * idle_duration        (long) // This is deprecated and used to circumvent b/26355386.
          * </pre>
          *
          * <p>
@@ -7457,6 +7521,14 @@
         }
 
         /**
+         * Get the key that retrieves a bluetooth a2dp src's priority.
+         * @hide
+         */
+        public static final String getBluetoothA2dpSrcPriorityKey(String address) {
+            return BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+        }
+
+        /**
          * Get the key that retrieves a bluetooth Input Device's priority.
          * @hide
          */
@@ -8236,7 +8308,6 @@
         /**
          * Whether to enable contacts metadata syncing or not
          * The value 1 - enable, 0 - disable
-         * @hide
          */
         public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
 
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 46aa1af..37ec725 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -68,7 +68,6 @@
      * TLS or STARTTLS) is permitted for communicating with {@code hostname} for this process.
      *
      * @see #isCleartextTrafficPermitted()
-     * @hide
      */
     public boolean isCleartextTrafficPermitted(String hostname) {
         return libcore.net.NetworkSecurityPolicy.getInstance()
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 71d9d5d..4de36cd 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -120,6 +120,32 @@
         return mTrustManager;
     }
 
+    /**
+     * Returns {@code true} if cleartext traffic is permitted for this application, which is the
+     * case only if all configurations permit cleartext traffic. For finer-grained policy use
+     * {@link #isCleartextTrafficPermitted(String)}.
+     */
+    public boolean isCleartextTrafficPermitted() {
+        ensureInitialized();
+        if (mConfigs != null) {
+            for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) {
+                if (!entry.second.isCleartextTrafficPermitted()) {
+                    return false;
+                }
+            }
+        }
+
+        return mDefaultConfig.isCleartextTrafficPermitted();
+    }
+
+    /**
+     * Returns {@code true} if cleartext traffic is permitted for this application when connecting
+     * to {@code hostname}.
+     */
+    public boolean isCleartextTrafficPermitted(String hostname) {
+        return getConfigForHostname(hostname).isCleartextTrafficPermitted();
+    }
+
     private void ensureInitialized() {
         synchronized(mLock) {
             if (mInitialized) {
diff --git a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
new file mode 100644
index 0000000..e7d17c2
--- /dev/null
+++ b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2015, 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.security.net.config;
+
+/**
+ * {@link libcore.net.NetworkSecurityPolicy} based on an {@link ApplicationConfig}.
+ *
+ * @hide
+ */
+public class ConfigNetworkSecurityPolicy extends libcore.net.NetworkSecurityPolicy {
+    private final ApplicationConfig mConfig;
+
+    public ConfigNetworkSecurityPolicy(ApplicationConfig config) {
+        mConfig = config;
+    }
+
+    @Override
+    public boolean isCleartextTrafficPermitted() {
+        return mConfig.isCleartextTrafficPermitted();
+    }
+
+    @Override
+    public boolean isCleartextTrafficPermitted(String hostname) {
+        return mConfig.isCleartextTrafficPermitted(hostname);
+    }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
index 5ebc7ac..0f66873 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -40,5 +40,6 @@
             throw new RuntimeException("Failed to install provider as highest priority provider."
                     + " Provider was installed at position " + pos);
         }
+        libcore.net.NetworkSecurityPolicy.setInstance(new ConfigNetworkSecurityPolicy(config));
     }
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index bb46e83..816ecde 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -15,9 +15,6 @@
  */
 package android.service.dreams;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
 import android.annotation.Nullable;
@@ -33,27 +30,32 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.MathUtils;
 import android.util.Slog;
 import android.view.ActionMode;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
 import android.view.SearchEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
-import android.util.MathUtils;
 
+import com.android.internal.policy.PhoneWindow;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.DumpUtils.Dump;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
 /**
  * Extend this class to implement a custom dream (available to the user as a "Daydream").
  *
@@ -365,6 +367,11 @@
     @Override
     public void onActionModeFinished(ActionMode mode) {
     }
+
+    /** {@inheritDoc} */
+    @Override
+    public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+    }
     // end Window.Callback methods
 
     // begin public api
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 88bd283..eff09d6 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -85,6 +85,13 @@
             "android.service.zen.automatic.configurationActivity";
 
     /**
+     * The name of the {@code meta-data} tag containing the maximum number of rule instances that
+     * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
+     */
+    public static final String META_DATA_RULE_INSTANCE_LIMIT =
+            "android.service.zen.automatic.ruleInstanceLimit";
+
+    /**
      * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
      */
     public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
diff --git a/core/java/android/service/notification/INotificationAssistant.aidl b/core/java/android/service/notification/INotificationAssistant.aidl
deleted file mode 100644
index 5c5f358..0000000
--- a/core/java/android/service/notification/INotificationAssistant.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2015, 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.service.notification;
-
-import android.service.notification.NotificationAdjustment;
-import android.service.notification.IStatusBarNotificationHolder;
-import android.service.notification.NotificationRankingUpdate;
-
-/** @hide */
-interface INotificationAssistant
-{
-    void onListenerConnected(in NotificationRankingUpdate update);
-    void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
-            in NotificationRankingUpdate update);
-    void onNotificationRankingUpdate(in NotificationRankingUpdate update);
-    void onListenerHintsChanged(int hints);
-    void onInterruptionFilterChanged(int interruptionFilter);
-    NotificationAdjustment onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
-    void onNotificationVisibilityChanged(String key, long time, boolean visible);
-    void onNotificationClick(String key, long time);
-    void onNotificationActionClick(String key, long time, int actionIndex);
-    void onNotificationRemoved(String key, long time, int reason);
-}
\ No newline at end of file
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index e6bf6ba..a0de17f 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -23,6 +23,7 @@
 /** @hide */
 oneway interface INotificationListener
 {
+    // listeners and assistants
     void onListenerConnected(in NotificationRankingUpdate update);
     void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
             in NotificationRankingUpdate update);
@@ -31,4 +32,11 @@
     void onNotificationRankingUpdate(in NotificationRankingUpdate update);
     void onListenerHintsChanged(int hints);
     void onInterruptionFilterChanged(int interruptionFilter);
+
+    // assistants only
+    void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
+    void onNotificationVisibilityChanged(String key, long time, boolean visible);
+    void onNotificationClick(String key, long time);
+    void onNotificationActionClick(String key, long time, int actionIndex);
+    void onNotificationRemovedReason(String key, long time, int reason);
 }
diff --git a/core/java/android/service/notification/NotificationAdjustment.java b/core/java/android/service/notification/NotificationAdjustment.java
deleted file mode 100644
index c5f0db9..0000000
--- a/core/java/android/service/notification/NotificationAdjustment.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package android.service.notification;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class NotificationAdjustment implements Parcelable {
-    int mImportance;
-    CharSequence mExplanation;
-    Uri mReference;
-
-    /**
-     * Create a notification importance adjustment.
-     *
-     * @param importance The final importance of the notification.
-     * @param explanation A human-readable justification for the adjustment.
-     * @param reference A reference to an external object that augments the
-     *                  explanation, such as a
-     *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
-     *                  or null.
-     */
-    public NotificationAdjustment(int importance, CharSequence explanation, Uri reference) {
-        mImportance = importance;
-        mExplanation = explanation;
-        mReference = reference;
-    }
-
-    private NotificationAdjustment(Parcel source) {
-        this(source.readInt(), source.readCharSequence(),
-                (Uri) source.readParcelable(NotificationAdjustment.class.getClassLoader()));
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mImportance);
-        dest.writeCharSequence(mExplanation);
-        dest.writeParcelable(mReference, 0);
-    }
-
-    public static final Parcelable.Creator<NotificationAdjustment> CREATOR
-            = new Parcelable.Creator<NotificationAdjustment>() {
-        @Override
-        public NotificationAdjustment createFromParcel(Parcel source) {
-            return new NotificationAdjustment(source);
-        }
-
-        @Override
-        public NotificationAdjustment[] newArray(int size) {
-            return new NotificationAdjustment[size];
-        }
-    };
-}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 7ce5e1f..035462d 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -17,11 +17,19 @@
 package android.service.notification;
 
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.INotificationManager;
 import android.app.Notification;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
 
 /**
  * A service that helps the user manage notifications by modifying the
@@ -39,6 +47,8 @@
  * &lt;/service></pre>
  */
 public abstract class NotificationAssistantService extends NotificationListenerService {
+    private static final String TAG = "NotificationAssistant";
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
@@ -85,6 +95,39 @@
     /** Notification was canceled because it was an invisible member of a group. */
     public static final int REASON_GROUP_OPTIMIZATION = 13;
 
+    /** Notification was canceled by the user banning the topic. */
+    public static final int REASON_TOPIC_BANNED = 14;
+
+    public class Adjustment {
+        int mImportance;
+        CharSequence mExplanation;
+        Uri mReference;
+
+        /**
+         * Create a notification importance adjustment.
+         *
+         * @param importance The final importance of the notification.
+         * @param explanation A human-readable justification for the adjustment.
+         * @param reference A reference to an external object that augments the
+         *                  explanation, such as a
+         *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
+         *                  or null.
+         */
+        public Adjustment(int importance, CharSequence explanation, Uri reference) {
+            mImportance = importance;
+            mExplanation = explanation;
+            mReference = reference;
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (mWrapper == null) {
+            mWrapper = new NotificationAssistantWrapper();
+        }
+        return mWrapper;
+    }
+
     /**
      * A notification was posted by an app. Called before alert.
      *
@@ -93,7 +136,7 @@
      * @param user true if the initial importance reflects an explicit user preference.
      * @return an adjustment or null to take no action, within 100ms.
      */
-    abstract public NotificationAdjustment onNotificationEnqueued(StatusBarNotification sbn,
+    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
           int importance, boolean user);
 
     /**
@@ -150,9 +193,15 @@
      * @param key the notification key
      * @param adjustment the new importance with an explanation
      */
-    public final void adjustImportance(String key, NotificationAdjustment adjustment)
+    public final void adjustImportance(String key, Adjustment adjustment)
     {
-        // TODO: pack up the adjustment and send it to the NotificationManager.
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
+                    adjustment.mImportance, adjustment.mExplanation);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
     }
 
     /**
@@ -160,7 +209,7 @@
      * be fired when the host notification is deleted, or when this annotation
      * is removed or replaced.
      *
-     * @param key the notification key
+     * @param key the key of the notification to be annotated
      * @param annotation the new annotation object
      */
     public final void setAnnotation(String key, Notification annotation)
@@ -171,10 +220,74 @@
     /**
      * Remove the annotation from a notification.
      *
-     * @param key the notification key
+     * @param key the key of the notification to be cleansed of annotatons
      */
     public final void clearAnnotation(String key)
     {
         // TODO: ask the NotificationManager to clear the annotation.
     }
+
+    private class NotificationAssistantWrapper extends NotificationListenerWrapper {
+        @Override
+        public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
+                                           int importance, boolean user) throws RemoteException {
+            StatusBarNotification sbn;
+            try {
+                sbn = sbnHolder.get();
+            } catch (RemoteException e) {
+                Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
+                return;
+            }
+
+            try {
+                Adjustment adjustment =
+                    NotificationAssistantService.this.onNotificationEnqueued(sbn, importance, user);
+                if (adjustment != null) {
+                    adjustImportance(sbn.getKey(), adjustment);
+                }
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onNotificationEnqueued", t);
+            }
+        }
+
+        @Override
+        public void onNotificationVisibilityChanged(String key, long time, boolean visible)
+                throws RemoteException {
+            try {
+                NotificationAssistantService.this.onNotificationVisibilityChanged(key, time,
+                        visible);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onNotificationVisibilityChanged", t);
+            }
+        }
+
+        @Override
+        public void onNotificationClick(String key, long time) throws RemoteException {
+            try {
+                NotificationAssistantService.this.onNotificationClick(key, time);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onNotificationClick", t);
+            }
+        }
+
+        @Override
+        public void onNotificationActionClick(String key, long time, int actionIndex)
+                throws RemoteException {
+            try {
+                NotificationAssistantService.this.onNotificationActionClick(key, time, actionIndex);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onNotificationActionClick", t);
+            }
+        }
+
+        @Override
+        public void onNotificationRemovedReason(String key, long time, int reason)
+                throws RemoteException {
+            try {
+                NotificationAssistantService.this.onNotificationRemoved(key, time, reason);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onNotificationRemoved", t);
+            }
+        }
+    }
 }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 1e62edc..ed90e79 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -15,6 +15,7 @@
  */
 
 package android.service.notification;
+import android.service.notification.IStatusBarNotificationHolder;
 
 import android.annotation.SystemApi;
 import android.annotation.SdkConstant;
@@ -122,6 +123,8 @@
             NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
     public static final int SUPPRESSED_EFFECT_PEEK =
             NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+    public static final int SUPPRESSED_EFFECT_SCREEN_ON =
+            NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 
     /**
      * The full trim of the StatusBarNotification including all its features.
@@ -151,7 +154,8 @@
     @SystemApi
     public static final int TRIM_LIGHT = 1;
 
-    private INotificationListenerWrapper mWrapper = null;
+    /** @hide */
+    protected NotificationListenerWrapper mWrapper = null;
     private RankingMap mRankingMap;
 
     private INotificationManager mNoMan;
@@ -291,7 +295,8 @@
         // optional
     }
 
-    private final INotificationManager getNotificationInterface() {
+    /** @hide */
+    protected final INotificationManager getNotificationInterface() {
         if (mNoMan == null) {
             mNoMan = INotificationManager.Stub.asInterface(
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -634,12 +639,13 @@
     @Override
     public IBinder onBind(Intent intent) {
         if (mWrapper == null) {
-            mWrapper = new INotificationListenerWrapper();
+            mWrapper = new NotificationListenerWrapper();
         }
         return mWrapper;
     }
 
-    private boolean isBound() {
+    /** @hide */
+    protected boolean isBound() {
         if (mWrapper == null) {
             Log.w(TAG, "Notification listener service not yet bound.");
             return false;
@@ -664,7 +670,7 @@
             int currentUser) throws RemoteException {
         mSystemContext = context;
         if (mWrapper == null) {
-            mWrapper = new INotificationListenerWrapper();
+            mWrapper = new NotificationListenerWrapper();
         }
         INotificationManager noMan = getNotificationInterface();
         noMan.registerListener(mWrapper, componentName, currentUser);
@@ -686,6 +692,36 @@
         }
     }
 
+    /**
+     * Request that the listener be rebound, after a previous call to (@link requestUnbind).
+     *
+     * <P>This method will fail for assistants that have
+     * not been granted the permission by the user.
+     *
+     * <P>The service should wait for the {@link #onListenerConnected()} event
+     * before performing any operations.
+     */
+    public static final void requestRebind(ComponentName componentName)
+            throws RemoteException {
+        INotificationManager noMan = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        noMan.requestBindListener(componentName);
+    }
+
+    /**
+     * Request that the service be unbound.
+     *
+     * <P>This will no longer receive updates until
+     * {@link #requestRebind(ComponentName)} is called.
+     * The service will likely be kiled by the system after this call.
+     */
+    public final void requestUnbind() throws RemoteException {
+        if (mWrapper != null) {
+            INotificationManager noMan = getNotificationInterface();
+            noMan.requestUnbindListener(mWrapper);
+        }
+    }
+
     /** Convert new-style Icons to legacy representations for pre-M clients. */
     private void createLegacyIconExtras(Notification n) {
         Icon smallIcon = n.getSmallIcon();
@@ -716,7 +752,8 @@
         }
     }
 
-    private class INotificationListenerWrapper extends INotificationListener.Stub {
+    /** @hide */
+    protected class NotificationListenerWrapper extends INotificationListener.Stub {
         @Override
         public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
                 NotificationRankingUpdate update) {
@@ -817,6 +854,35 @@
                 Log.w(TAG, "Error running onInterruptionFilterChanged", t);
             }
         }
+
+        @Override
+        public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder,
+                                           int importance, boolean user) throws RemoteException {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onNotificationVisibilityChanged(String key, long time, boolean visible)
+                throws RemoteException {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onNotificationClick(String key, long time) throws RemoteException {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onNotificationActionClick(String key, long time, int actionIndex)
+                throws RemoteException {
+            // no-op in the listener
+        }
+
+        @Override
+        public void onNotificationRemovedReason(String key, long time, int reason)
+                throws RemoteException {
+            // no-op in the listener
+        }
     }
 
     private void applyUpdate(NotificationRankingUpdate update) {
@@ -927,7 +993,8 @@
 
         /**
          * Returns the type(s) of visual effects that should be suppressed for this notification.
-         * See {@link #SUPPRESSED_EFFECT_LIGHTS}, {@link #SUPPRESSED_EFFECT_PEEK}}.
+         * See {@link #SUPPRESSED_EFFECT_LIGHTS}, {@link #SUPPRESSED_EFFECT_PEEK},
+         * {@link #SUPPRESSED_EFFECT_SCREEN_ON}.
          */
         public int getSuppressedVisualEffects() {
             return mSuppressedVisualEffects;
@@ -965,13 +1032,12 @@
             return mImportanceExplanation;
         }
 
-        private void populate(String key, int rank, boolean isAmbient,
-                boolean matchesInterruptionFilter, int visibilityOverride,
-                int suppressedVisualEffects, int importance,
+        private void populate(String key, int rank, boolean matchesInterruptionFilter,
+                int visibilityOverride, int suppressedVisualEffects, int importance,
                 CharSequence explanation) {
             mKey = key;
             mRank = rank;
-            mIsAmbient = isAmbient;
+            mIsAmbient = importance < IMPORTANCE_DEFAULT;
             mMatchesInterruptionFilter = matchesInterruptionFilter;
             mVisibilityOverride = visibilityOverride;
             mSuppressedVisualEffects = suppressedVisualEffects;
@@ -1042,7 +1108,7 @@
          */
         public boolean getRanking(String key, Ranking outRanking) {
             int rank = getRank(key);
-            outRanking.populate(key, rank, isAmbient(key), !isIntercepted(key),
+            outRanking.populate(key, rank, !isIntercepted(key),
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key));
             return rank >= 0;
@@ -1058,15 +1124,6 @@
             return rank != null ? rank : -1;
         }
 
-        private boolean isAmbient(String key) {
-            int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex();
-            if (firstAmbientIndex < 0) {
-                return false;
-            }
-            int rank = getRank(key);
-            return rank >= 0 && rank >= firstAmbientIndex;
-        }
-
         private boolean isIntercepted(String key) {
             synchronized (this) {
                 if (mIntercepted == null) {
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 0d42ffb..79f6fc4 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -26,17 +26,15 @@
     // TODO: Support incremental updates.
     private final String[] mKeys;
     private final String[] mInterceptedKeys;
-    private final int mFirstAmbientIndex;
     private final Bundle mVisibilityOverrides;
     private final Bundle mSuppressedVisualEffects;
     private final int[] mImportance;
     private final Bundle mImportanceExplanation;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
-            Bundle visibilityOverrides, int firstAmbientIndex, Bundle suppressedVisualEffects,
+            Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation) {
         mKeys = keys;
-        mFirstAmbientIndex = firstAmbientIndex;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
         mSuppressedVisualEffects = suppressedVisualEffects;
@@ -46,7 +44,6 @@
 
     public NotificationRankingUpdate(Parcel in) {
         mKeys = in.readStringArray();
-        mFirstAmbientIndex = in.readInt();
         mInterceptedKeys = in.readStringArray();
         mVisibilityOverrides = in.readBundle();
         mSuppressedVisualEffects = in.readBundle();
@@ -63,7 +60,6 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeStringArray(mKeys);
-        out.writeInt(mFirstAmbientIndex);
         out.writeStringArray(mInterceptedKeys);
         out.writeBundle(mVisibilityOverrides);
         out.writeBundle(mSuppressedVisualEffects);
@@ -86,10 +82,6 @@
         return mKeys;
     }
 
-    public int getFirstAmbientIndex() {
-        return mFirstAmbientIndex;
-    }
-
     public String[] getInterceptedKeys() {
         return mInterceptedKeys;
     }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 541623d..4688843 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -79,6 +79,7 @@
     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
     private static final boolean DEFAULT_ALLOW_PEEK = true;
     private static final boolean DEFAULT_ALLOW_LIGHTS = true;
+    private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
 
     private static final int XML_VERSION = 2;
     private static final String ZEN_TAG = "zen";
@@ -95,6 +96,7 @@
     private static final String ALLOW_ATT_EVENTS = "events";
     private static final String ALLOW_ATT_PEEK = "peek";
     private static final String ALLOW_ATT_LIGHTS = "lights";
+    private static final String ALLOW_ATT_SCREEN_ON = "screen_on";
 
     private static final String CONDITION_TAG = "condition";
     private static final String CONDITION_ATT_COMPONENT = "component";
@@ -128,6 +130,7 @@
     public int user = UserHandle.USER_SYSTEM;
     public boolean allowPeek = DEFAULT_ALLOW_PEEK;
     public boolean allowLights = DEFAULT_ALLOW_LIGHTS;
+    public boolean allowScreenOn = DEFAULT_ALLOW_SCREEN_ON;
 
     public ZenRule manualRule;
     public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -156,6 +159,7 @@
         }
         allowPeek = source.readInt() == 1;
         allowLights = source.readInt() == 1;
+        allowScreenOn = source.readInt() == 1;
     }
 
     @Override
@@ -185,6 +189,7 @@
         }
         dest.writeInt(allowPeek ? 1 : 0);
         dest.writeInt(allowLights ? 1 : 0);
+        dest.writeInt(allowScreenOn ? 1 : 0);
     }
 
     @Override
@@ -200,6 +205,7 @@
                 .append(",allowEvents=").append(allowEvents)
                 .append(",allowPeek=").append(allowPeek)
                 .append(",allowLights=").append(allowLights)
+                .append(",allowScreenOn=").append(allowScreenOn)
                 .append(",automaticRules=").append(automaticRules)
                 .append(",manualRule=").append(manualRule)
                 .append(']').toString();
@@ -240,6 +246,9 @@
         if (allowLights != to.allowLights) {
             d.addLine("allowLights", allowLights, to.allowLights);
         }
+        if (allowScreenOn != to.allowScreenOn) {
+            d.addLine("allowScreenOn", allowScreenOn, to.allowScreenOn);
+        }
         final ArraySet<String> allRules = new ArraySet<>();
         addKeys(allRules, automaticRules);
         addKeys(allRules, to.automaticRules);
@@ -339,6 +348,7 @@
                 && other.allowEvents == allowEvents
                 && other.allowPeek == allowPeek
                 && other.allowLights == allowLights
+                && other.allowScreenOn == allowScreenOn
                 && other.user == user
                 && Objects.equals(other.automaticRules, automaticRules)
                 && Objects.equals(other.manualRule, manualRule);
@@ -348,7 +358,7 @@
     public int hashCode() {
         return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
                 allowMessagesFrom, allowReminders, allowEvents, allowPeek, allowLights,
-                user, automaticRules, manualRule);
+                allowScreenOn, user, automaticRules, manualRule);
     }
 
     private static String toDayList(int[] days) {
@@ -435,6 +445,8 @@
                     }
                     rt.allowPeek = safeBoolean(parser, ALLOW_ATT_PEEK, DEFAULT_ALLOW_PEEK);
                     rt.allowLights = safeBoolean(parser, ALLOW_ATT_LIGHTS, DEFAULT_ALLOW_LIGHTS);
+                    rt.allowScreenOn =
+                            safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
                 } else if (MANUAL_TAG.equals(tag)) {
                     rt.manualRule = readRuleXml(parser);
                 } else if (AUTOMATIC_TAG.equals(tag)) {
@@ -465,6 +477,7 @@
         out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
         out.attribute(null, ALLOW_ATT_PEEK, Boolean.toString(allowPeek));
         out.attribute(null, ALLOW_ATT_LIGHTS, Boolean.toString(allowLights));
+        out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowScreenOn));
         out.endTag(null, ALLOW_TAG);
 
         if (manualRule != null) {
@@ -643,6 +656,9 @@
         if (!allowLights) {
             suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
         }
+        if (!allowScreenOn) {
+            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
+        }
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
@@ -681,6 +697,8 @@
         if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
             allowPeek = (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_PEEK) == 0;
             allowLights = (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_LIGHTS) == 0;
+            allowScreenOn =
+                    (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0;
         }
     }
 
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
index 7e70501..4bfc948 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -16,6 +16,7 @@
 package android.service.quicksettings;
 
 import android.content.ComponentName;
+import android.graphics.drawable.Icon;
 import android.service.quicksettings.Tile;
 
 /**
@@ -23,5 +24,12 @@
  */
 interface IQSService {
     void updateQsTile(in Tile tile);
+    void updateStatusIcon(in Tile tile, in Icon icon,
+            String contentDescription);
     void onShowDialog(in Tile tile);
+    void onStartActivity(in Tile tile);
+    void setTileMode(in ComponentName component, int mode);
+    boolean isLocked();
+    boolean isSecure();
+    void startUnlockAndRun(in Tile tile);
 }
diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl
index 63a4c5e..bfde870 100644
--- a/core/java/android/service/quicksettings/IQSTileService.aidl
+++ b/core/java/android/service/quicksettings/IQSTileService.aidl
@@ -22,10 +22,12 @@
  * @hide
  */
 oneway interface IQSTileService {
+    void setQSService(in IQSService service);
     void setQSTile(in Tile tile);
     void onTileAdded();
     void onTileRemoved();
     void onStartListening();
     void onStopListening();
     void onClick(IBinder wtoken);
+    void onUnlockComplete();
 }
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index a53fc59..85f1955 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -36,11 +36,39 @@
 
     private static final String TAG = "Tile";
 
+    /**
+     * This is the default state of any tile, until updated by the {@link TileService}.
+     * <p>
+     * An unavailable state indicates that for some reason this tile is not currently
+     * available to the user for some reason, and will have no click action.  The tile's
+     * icon will be tinted differently to reflect this state.
+     */
+    public static final int STATE_UNAVAILABLE = 0;
+
+    /**
+     * This represents a tile that is currently in a disabled state but is still interactable.
+     *
+     * A disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
+     * bluetooth disabled), but is still interactable by the user to modify this state.  Tiles
+     * that have boolean states should use this to represent one of their states.  The tile's
+     * icon will be tinted differently to reflect this state, but still be distinct from unavailable.
+     */
+    public static final int STATE_INACTIVE = 1;
+
+    /**
+     * This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is on,
+     * cast is casting).
+     */
+    public static final int STATE_ACTIVE = 2;
+
     private ComponentName mComponentName;
-    private IQSService mService;
     private Icon mIcon;
     private CharSequence mLabel;
     private CharSequence mContentDescription;
+    // Default to active until clients of the new API can update.
+    private int mState = STATE_ACTIVE;
+
+    private IQSService mService;
 
     /**
      * @hide
@@ -52,8 +80,14 @@
     /**
      * @hide
      */
-    public Tile(ComponentName componentName, IQSService service) {
+    public Tile(ComponentName componentName) {
         mComponentName = componentName;
+    }
+
+    /**
+     * @hide
+     */
+    public void setService(IQSService service) {
         mService = service;
     }
 
@@ -65,6 +99,36 @@
     }
 
     /**
+     * @hide
+     */
+    public IQSService getQsService() {
+        return mService;
+    }
+
+    /**
+     * The current state of the tile.
+     *
+     * @see #STATE_UNAVAILABLE
+     * @see #STATE_INACTIVE
+     * @see #STATE_ACTIVE
+     */
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Sets the current state for the tile.
+     *
+     * Does not take effect until {@link #updateTile()} is called.
+     *
+     * @param state One of {@link #STATE_UNAVAILABLE}, {@link #STATE_INACTIVE},
+     * {@link #STATE_ACTIVE}
+     */
+    public void setState(int state) {
+        mState = state;
+    }
+
+    /**
      * Gets the current icon for the tile.
      */
     public Icon getIcon() {
@@ -137,21 +201,8 @@
         }
     }
 
-    /**
-     * @hide
-     * Notifies the IQSService that this tile is showing a dialog.
-     */
-    void onShowDialog() {
-        try {
-            mService.onShowDialog(this);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't onShowDialog");
-        }
-    }
-
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongInterface(mService);
         if (mComponentName != null) {
             dest.writeByte((byte) 1);
             mComponentName.writeToParcel(dest, flags);
@@ -164,12 +215,12 @@
         } else {
             dest.writeByte((byte) 0);
         }
+        dest.writeInt(mState);
         TextUtils.writeToParcel(mLabel, dest, flags);
         TextUtils.writeToParcel(mContentDescription, dest, flags);
     }
 
     private void readFromParcel(Parcel source) {
-        mService = IQSService.Stub.asInterface(source.readStrongBinder());
         if (source.readByte() != 0) {
             mComponentName = ComponentName.CREATOR.createFromParcel(source);
         } else {
@@ -180,6 +231,7 @@
         } else {
             mIcon = null;
         }
+        mState = source.readInt();
         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
     }
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index d8787b4..c02465b 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -15,9 +15,14 @@
  */
 package android.service.quicksettings;
 
+import android.Manifest;
+import android.annotation.SystemApi;
 import android.app.Dialog;
 import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -26,29 +31,29 @@
 import android.view.WindowManager;
 
 /**
- * A QSTileService provides the user a tile that can be added to Quick Settings.
+ * A TileService provides the user a tile that can be added to Quick Settings.
  * Quick Settings is a space provided that allows the user to change settings and
  * take quick actions without leaving the context of their current app.
  *
- * <p>The lifecycle of a QSTileService is different from some other services in
+ * <p>The lifecycle of a TileService is different from some other services in
  * that it may be unbound during parts of its lifecycle.  Any of the following
  * lifecycle events can happen indepently in a separate binding/creation of the
  * service.</p>
  *
  * <ul>
- * <li>When a tile is added by the user its QSTileService will be bound to and
+ * <li>When a tile is added by the user its TileService will be bound to and
  * {@link #onTileAdded()} will be called.</li>
  *
  * <li>When a tile should be up to date and listing will be indicated by
  * {@link #onStartListening()} and {@link #onStopListening()}.</li>
  *
- * <li>When the user removes a tile from Quick Settings {@link #onStopListening()}
+ * <li>When the user removes a tile from Quick Settings {@link #onTileRemoved()}
  * will be called.</li>
  * </ul>
- * <p>QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
+ * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
  * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
  * The label and icon for the service will be used as the default label and
- * icon for the tile. Here is an example QSTileService declaration.</p>
+ * icon for the tile. Here is an example TileService declaration.</p>
  * <pre class="prettyprint">
  * {@literal
  * <service
@@ -67,15 +72,53 @@
 public class TileService extends Service {
 
     /**
-     * Action that identifies a Service as being a QSTileService.
+     * Action that identifies a Service as being a TileService.
      */
     public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
 
+    /**
+     * The tile mode hasn't been set yet.
+     * @hide
+     */
+    public static final int TILE_MODE_UNSET = 0;
+
+    /**
+     * Constant to be returned by {@link #onTileAdded}.
+     * <p>
+     * Passive mode is the default mode for tiles.  The System will tell the tile
+     * when it is most important to update by putting it in the listening state.
+     */
+    public static final int TILE_MODE_PASSIVE = 1;
+
+    /**
+     * Constant to be returned by {@link #onTileAdded}.
+     * <p>
+     * Active mode is for tiles which already listen and keep track of their state in their
+     * own process.  These tiles may request to send an update to the System while their process
+     * is alive using {@link #requestListeningState}.  The System will only bind these tiles
+     * on its own when a click needs to occur.
+     */
+    public static final int TILE_MODE_ACTIVE = 2;
+
+    /**
+     * Used to notify SysUI that Listening has be requested.
+     * @hide
+     */
+    public static final String ACTION_REQUEST_LISTENING
+            = "android.service.quicksettings.action.REQUEST_LISTENING";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
+
     private final H mHandler = new H(Looper.getMainLooper());
 
     private boolean mListening = false;
     private Tile mTile;
     private IBinder mToken;
+    private IQSService mService;
+    private Runnable mUnlockRunnable;
 
     @Override
     public void onDestroy() {
@@ -92,8 +135,12 @@
      * Note that this is not guaranteed to be called between {@link #onCreate()}
      * and {@link #onStartListening()}, it will only be called when the tile is added
      * and not on subsequent binds.
+     *
+     * @see #TILE_MODE_PASSIVE
+     * @see #TILE_MODE_ACTIVE
      */
-    public void onTileAdded() {
+    public int onTileAdded() {
+        return TILE_MODE_PASSIVE;
     }
 
     /**
@@ -128,17 +175,103 @@
     }
 
     /**
+     * Sets an icon to be shown in the status bar.
+     * <p>
+     * The icon will be displayed before all other icons.  Can only be called between
+     * {@link #onStartListening} and {@link #onStopListening}.  Can only be called by system apps.
+     *
+     * @param icon The icon to be displayed, null to hide
+     * @param contentDescription Content description of the icon to be displayed
+     * @hide
+     */
+    @SystemApi
+    public final void setStatusIcon(Icon icon, String contentDescription) {
+        if (mService != null) {
+            try {
+                mService.updateStatusIcon(mTile, icon, contentDescription);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
      * Used to show a dialog.
      *
      * This will collapse the Quick Settings panel and show the dialog.
      *
      * @param dialog Dialog to show.
+     *
+     * @see #isLocked()
      */
     public final void showDialog(Dialog dialog) {
         dialog.getWindow().getAttributes().token = mToken;
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG);
         dialog.show();
-        getQsTile().onShowDialog();
+        try {
+            mService.onShowDialog(mTile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Prompts the user to unlock the device before executing the Runnable.
+     * <p>
+     * The user will be prompted for their current security method if applicable
+     * and if successful, runnable will be executed.  The Runnable will not be
+     * executed if the user fails to unlock the device or cancels the operation.
+     */
+    public final void unlockAndRun(Runnable runnable) {
+        mUnlockRunnable = runnable;
+        try {
+            mService.startUnlockAndRun(mTile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Checks if the device is in a secure state.
+     *
+     * TileServices should detect when the device is secure and change their behavior
+     * accordingly.
+     *
+     * @return true if the device is secure.
+     */
+    public final boolean isSecure() {
+        try {
+            return mService.isSecure();
+        } catch (RemoteException e) {
+            return true;
+        }
+    }
+
+    /**
+     * Checks if the lock screen is showing.
+     *
+     * When a device is locked, then {@link #showDialog} will not present a dialog, as it will
+     * be under the lock screen. If the behavior of the Tile is safe to do while locked,
+     * then the user should use {@link #startActivity} to launch an activity on top of the lock
+     * screen, otherwise the tile should use {@link #unlockAndRun(Runnable)} to give the
+     * user their security challenge.
+     *
+     * @return true if the device is locked.
+     */
+    public final boolean isLocked() {
+        try {
+            return mService.isLocked();
+        } catch (RemoteException e) {
+            return true;
+        }
+    }
+
+    /**
+     * Start an activity while collapsing the panel.
+     */
+    public final void startActivityAndCollapse(Intent intent) {
+        startActivity(intent);
+        try {
+            mService.onStartActivity(mTile);
+        } catch (RemoteException e) {
+        }
     }
 
     /**
@@ -156,6 +289,11 @@
     public IBinder onBind(Intent intent) {
         return new IQSTileService.Stub() {
             @Override
+            public void setQSService(IQSService service) throws RemoteException {
+                mHandler.obtainMessage(H.MSG_SET_SERVICE, service).sendToTarget();
+            }
+
+            @Override
             public void setQSTile(Tile tile) throws RemoteException {
                 mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget();
             }
@@ -184,6 +322,11 @@
             public void onClick(IBinder wtoken) throws RemoteException {
                 mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget();
             }
+
+            @Override
+            public void onUnlockComplete() throws RemoteException{
+                mHandler.sendEmptyMessage(H.MSG_UNLOCK_COMPLETE);
+            }
         };
     }
 
@@ -194,6 +337,8 @@
         private static final int MSG_TILE_ADDED = 4;
         private static final int MSG_TILE_REMOVED = 5;
         private static final int MSG_TILE_CLICKED = 6;
+        private static final int MSG_SET_SERVICE = 7;
+        private static final int MSG_UNLOCK_COMPLETE = 8;
 
         public H(Looper looper) {
             super(looper);
@@ -202,13 +347,34 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case MSG_SET_SERVICE:
+                    mService = (IQSService) msg.obj;
+                    if (mTile != null) {
+                        mTile.setService(mService);
+                    }
+                    break;
                 case MSG_SET_TILE:
                     mTile = (Tile) msg.obj;
+                    if (mService != null && mTile != null) {
+                        mTile.setService(mService);
+                    }
                     break;
                 case MSG_TILE_ADDED:
-                    TileService.this.onTileAdded();
+                    int mode = TileService.this.onTileAdded();
+                    if (mService == null) {
+                        return;
+                    }
+                    try {
+                        mService.setTileMode(new ComponentName(TileService.this,
+                                TileService.this.getClass()), mode);
+                    } catch (RemoteException e) {
+                    }
                     break;
                 case MSG_TILE_REMOVED:
+                    if (mListening) {
+                        mListening = false;
+                        TileService.this.onStopListening();
+                    }
                     TileService.this.onTileRemoved();
                     break;
                 case MSG_STOP_LISTENING:
@@ -227,7 +393,24 @@
                     mToken = (IBinder) msg.obj;
                     TileService.this.onClick();
                     break;
+                case MSG_UNLOCK_COMPLETE:
+                    if (mUnlockRunnable != null) {
+                        mUnlockRunnable.run();
+                    }
+                    break;
             }
         }
     }
+
+    /**
+     * Requests that a tile be put in the listening state so it can send an update.
+     *
+     * This method is only applicable to tiles that return {@link #TILE_MODE_ACTIVE} from
+     * {@link #onTileAdded()}, and will do nothing otherwise.
+     */
+    public static final void requestListeningState(Context context, ComponentName component) {
+        Intent intent = new Intent(ACTION_REQUEST_LISTENING);
+        intent.putExtra(EXTRA_COMPONENT, component);
+        context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE);
+    }
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d146e5e..cd19607 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -167,6 +167,7 @@
         final Rect mDispatchedOutsets = new Rect();
         final Rect mFinalSystemInsets = new Rect();
         final Rect mFinalStableInsets = new Rect();
+        final Rect mBackdropFrame = new Rect();
         final Configuration mConfiguration = new Configuration();
 
         final WindowManager.LayoutParams mLayout
@@ -675,8 +676,8 @@
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
-                            mVisibleInsets, mStableInsets, mOutsets, mConfiguration,
-                            mSurfaceHolder.mSurface);
+                            mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
+                            mConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
diff --git a/core/java/android/text/Editable.java b/core/java/android/text/Editable.java
index a284a00..b3f2c2a 100644
--- a/core/java/android/text/Editable.java
+++ b/core/java/android/text/Editable.java
@@ -40,10 +40,14 @@
      * is Spanned, the spans from it are preserved into the Editable.
      * Existing spans within the Editable that entirely cover the replaced
      * range are retained, but any that were strictly within the range
-     * that was replaced are removed.  As a special case, the cursor
-     * position is preserved even when the entire range where it is
-     * located is replaced.
+     * that was replaced are removed. If the <code>source</code> contains a span
+     * with {@link Spanned#SPAN_PARAGRAPH} flag, and it does not satisfy the
+     * paragraph boundary constraint, it is not retained. As a special case, the
+     * cursor position is preserved even when the entire range where it is located
+     * is replaced.
      * @return  a reference to this object.
+     *
+     * @see Spanned#SPAN_PARAGRAPH
      */
     public Editable replace(int st, int en, CharSequence source, int start, int end);
 
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index f9387b3..2c4241b 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1116,24 +1116,30 @@
     public int getOffsetForHorizontal(int line, float horiz) {
         // TODO: use Paint.getOffsetForAdvance to avoid binary search
         final int lineEndOffset = getLineEnd(line);
+        final int lineStartOffset = getLineStart(line);
+
+        Directions dirs = getLineDirections(line);
+
+        TextLine tl = TextLine.obtain();
+        // XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here.
+        tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs,
+                false, null);
+
         final int max;
         if (line == getLineCount() - 1) {
             max = lineEndOffset;
         } else {
-            max = mPaint.getTextRunCursor(mText, 0, mText.length(),
-                    isRtlCharAt(lineEndOffset) ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR,
-                    lineEndOffset, Paint.CURSOR_BEFORE);
+            max = tl.getOffsetToLeftRightOf(lineEndOffset - lineStartOffset,
+                    !isRtlCharAt(lineEndOffset - 1)) + lineStartOffset;
         }
-        final int min = getLineStart(line);
-        Directions dirs = getLineDirections(line);
-
-        int best = min;
+        int best = lineStartOffset;
         float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
 
         for (int i = 0; i < dirs.mDirections.length; i += 2) {
-            int here = min + dirs.mDirections[i];
+            int here = lineStartOffset + dirs.mDirections[i];
             int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
-            int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1;
+            boolean isRtl = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0;
+            int swap = isRtl ? -1 : 1;
 
             if (there > max)
                 there = max;
@@ -1153,23 +1159,23 @@
                 low = here + 1;
 
             if (low < there) {
-                low = getOffsetAtStartOf(low);
+                int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset;
+                low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset;
+                if (low >= here && low < there) {
+                    float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
+                    if (aft < there) {
+                        float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
 
-                float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
-
-                int aft = TextUtils.getOffsetAfter(mText, low);
-                if (aft < there) {
-                    float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
-
-                    if (other < dist) {
-                        dist = other;
-                        low = aft;
+                        if (other < dist) {
+                            dist = other;
+                            low = aft;
+                        }
                     }
-                }
 
-                if (dist < bestdist) {
-                    bestdist = dist;
-                    best = low;
+                    if (dist < bestdist) {
+                        bestdist = dist;
+                        best = low;
+                    }
                 }
             }
 
@@ -1188,6 +1194,7 @@
             best = max;
         }
 
+        TextLine.recycle(tl);
         return best;
     }
 
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 40315ad..4267238 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -421,8 +421,17 @@
 
                 // Add span only if this object is not yet used as a span in this string
                 if (getSpanStart(spans[i]) < 0) {
-                    setSpan(false, spans[i], st - csStart + start, en - csStart + start,
-                            sp.getSpanFlags(spans[i]) | SPAN_ADDED);
+                    int copySpanStart = st - csStart + start;
+                    int copySpanEnd = en - csStart + start;
+                    int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED;
+
+                    int flagsStart = (copySpanFlags & START_MASK) >> START_SHIFT;
+                    int flagsEnd = copySpanFlags & END_MASK;
+
+                    if(!isInvalidParagraphStart(copySpanStart, flagsStart) &&
+                            !isInvalidParagraphEnd(copySpanEnd, flagsEnd)) {
+                        setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags);
+                    }
                 }
             }
             restoreInvariants();
@@ -666,23 +675,13 @@
         checkRange("setSpan", start, end);
 
         int flagsStart = (flags & START_MASK) >> START_SHIFT;
-        if (flagsStart == PARAGRAPH) {
-            if (start != 0 && start != length()) {
-                char c = charAt(start - 1);
-
-                if (c != '\n')
-                    throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
-            }
+        if(isInvalidParagraphStart(start, flagsStart)) {
+            throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
         }
 
         int flagsEnd = flags & END_MASK;
-        if (flagsEnd == PARAGRAPH) {
-            if (end != 0 && end != length()) {
-                char c = charAt(end - 1);
-
-                if (c != '\n')
-                    throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
-            }
+        if(isInvalidParagraphEnd(end, flagsEnd)) {
+            throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
         }
 
         // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@@ -761,6 +760,28 @@
         }
     }
 
+    private final boolean isInvalidParagraphStart(int start, int flagsStart) {
+        if (flagsStart == PARAGRAPH) {
+            if (start != 0 && start != length()) {
+                char c = charAt(start - 1);
+
+                if (c != '\n') return true;
+            }
+        }
+        return false;
+    }
+
+    private final boolean isInvalidParagraphEnd(int end, int flagsEnd) {
+        if (flagsEnd == PARAGRAPH) {
+            if (end != 0 && end != length()) {
+                char c = charAt(end - 1);
+
+                if (c != '\n') return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Remove the specified markup object from the buffer.
      */
diff --git a/core/java/android/text/Spanned.java b/core/java/android/text/Spanned.java
index a785d1b..a0d54c2 100644
--- a/core/java/android/text/Spanned.java
+++ b/core/java/android/text/Spanned.java
@@ -81,7 +81,9 @@
      * immediately after a \n character, and if the \n
      * that anchors it is deleted, the endpoint is pulled to the
      * next \n that follows in the buffer (or to the end of
-     * the buffer).
+     * the buffer). If a span with SPAN_PARAGRAPH flag is pasted
+     * into another text and the paragraph boundary constraint
+     * is not satisfied, the span is discarded.
      */
     public static final int SPAN_PARAGRAPH =   0x33;
 
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index c42c791..2a52961 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -688,13 +688,14 @@
      * @param bottom the bottom of the line
      * @param fmi receives metrics information, can be null
      * @param needWidth true if the width of the run is needed
+     * @param offset the offset for the purpose of measuring
      * @return the signed width of the run based on the run direction; only
      * valid if needWidth is true
      */
     private float handleText(TextPaint wp, int start, int end,
             int contextStart, int contextEnd, boolean runIsRtl,
             Canvas c, float x, int top, int y, int bottom,
-            FontMetricsInt fmi, boolean needWidth) {
+            FontMetricsInt fmi, boolean needWidth, int offset) {
 
         // Get metrics first (even for empty strings or "0" width runs)
         if (fmi != null) {
@@ -712,11 +713,11 @@
         if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
             if (mCharsValid) {
                 ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd,
-                        runIsRtl, end);
+                        runIsRtl, offset);
             } else {
                 int delta = mStart;
                 ret = wp.getRunAdvance(mText, delta + start, delta + end,
-                        delta + contextStart, delta + contextEnd, runIsRtl, delta + end);
+                        delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
             }
         }
 
@@ -865,8 +866,8 @@
             TextPaint wp = mWorkPaint;
             wp.set(mPaint);
             final int mlimit = measureLimit;
-            return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,
-                    y, bottom, fmi, needWidth || mlimit < measureLimit);
+            return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
+                    y, bottom, fmi, needWidth || mlimit < measureLimit, mlimit);
         }
 
         mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
@@ -910,13 +911,14 @@
             }
 
             for (int j = i, jnext; j < mlimit; j = jnext) {
-                jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
+                jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
                         mStart;
+                int offset = Math.min(jnext, mlimit);
 
                 wp.set(mPaint);
                 for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
                     // Intentionally using >= and <= as explained above
-                    if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
+                    if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) ||
                             (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
 
                     CharacterStyle span = mCharacterStyleSpanSet.spans[k];
@@ -928,7 +930,7 @@
                     wp.setHyphenEdit(0);
                 }
                 x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
-                        top, y, bottom, fmi, needWidth || jnext < measureLimit);
+                        top, y, bottom, fmi, needWidth || jnext < measureLimit, offset);
             }
         }
 
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index d9068dc..44811cb 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -150,6 +150,7 @@
                     ds[0].mX = event.getX();
                     ds[0].mY = event.getY();
 
+                    int nx = widget.getScrollX() + (int) dx;
                     int ny = widget.getScrollY() + (int) dy;
 
                     int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
@@ -161,6 +162,8 @@
                     int oldX = widget.getScrollX();
                     int oldY = widget.getScrollY();
 
+                    scrollTo(widget, layout, nx, ny);
+
                     // If we actually scrolled, then cancel the up action.
                     if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
                         widget.cancelLongPress();
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index d286231..117de774 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -16,30 +16,56 @@
 
 package android.text.style;
 
+import com.android.internal.util.Preconditions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Paint;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
+import android.util.LocaleList;
+
 import java.util.Locale;
 
 /**
  * Changes the {@link Locale} of the text to which the span is attached.
  */
 public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan {
-    private final Locale mLocale;
+    @NonNull
+    private final LocaleList mLocales;
 
     /**
-     * Creates a LocaleSpan.
-     * @param locale The {@link Locale} of the text to which the span is
-     * attached.
+     * Creates a {@link LocaleSpan} from a well-formed {@link Locale}.  Note that only
+     * {@link Locale} objects that can be created by {@link Locale#forLanguageTag(String)} are
+     * supported.
+     *
+     * <p><b>Caveat:</b> Do not specify any {@link Locale} object that cannot be created by
+     * {@link Locale#forLanguageTag(String)}.  {@code new Locale(" a ", " b c", " d")} is an
+     * example of such a malformed {@link Locale} object.</p>
+     *
+     * @param locale The {@link Locale} of the text to which the span is attached.
+     *
+     * @see #LocaleSpan(LocaleList)
      */
-    public LocaleSpan(Locale locale) {
-        mLocale = locale;
+    public LocaleSpan(@Nullable Locale locale) {
+        mLocales = new LocaleList(locale);
     }
 
-    public LocaleSpan(Parcel src) {
-        mLocale = new Locale(src.readString(), src.readString(), src.readString());
+    /**
+     * Creates a {@link LocaleSpan} from {@link LocaleList}.
+     *
+     * @param locales The {@link LocaleList} of the text to which the span is attached.
+     * @throws NullPointerException if {@code locales} is null
+     */
+    public LocaleSpan(@NonNull LocaleList locales) {
+        Preconditions.checkNotNull(locales, "locales cannot be null");
+        mLocales = locales;
+    }
+
+    public LocaleSpan(Parcel source) {
+        mLocales = LocaleList.CREATOR.createFromParcel(source);
     }
 
     @Override
@@ -64,31 +90,40 @@
 
     /** @hide */
     public void writeToParcelInternal(Parcel dest, int flags) {
-        dest.writeString(mLocale.getLanguage());
-        dest.writeString(mLocale.getCountry());
-        dest.writeString(mLocale.getVariant());
+        mLocales.writeToParcel(dest, flags);
     }
 
     /**
-     * Returns the {@link Locale}.
+     * @return The {@link Locale} for this span.  If multiple locales are associated with this
+     * span, only the first locale is returned.  {@code null} if no {@link Locale} is specified.
      *
-     * @return The {@link Locale} for this span.
+     * @see LocaleList#getPrimary()
+     * @see #getLocales()
      */
+    @Nullable
     public Locale getLocale() {
-        return mLocale;
+        return mLocales.getPrimary();
+    }
+
+    /**
+     * @return The entire list of locales that are associated with this span.
+     */
+    @NonNull
+    public LocaleList getLocales() {
+        return mLocales;
     }
 
     @Override
     public void updateDrawState(TextPaint ds) {
-        apply(ds, mLocale);
+        apply(ds, mLocales);
     }
 
     @Override
     public void updateMeasureState(TextPaint paint) {
-        apply(paint, mLocale);
+        apply(paint, mLocales);
     }
 
-    private static void apply(Paint paint, Locale locale) {
-        paint.setTextLocale(locale);
+    private static void apply(@NonNull Paint paint, @NonNull LocaleList locales) {
+        paint.setTextLocales(locales);
     }
 }
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 6b449f9..1b00db2 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -16,6 +16,8 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
@@ -84,7 +86,15 @@
 
     private int mFlags;
     private final String[] mSuggestions;
-    private final String mLocaleString;
+    /**
+     * Kept for compatibility for apps that rely on invalid locale strings e.g.
+     * {@code new Locale(" an ", " i n v a l i d ", "data")}, which cannot be handled by
+     * {@link #mLanguageTag}.
+     */
+    @NonNull
+    private final String mLocaleStringForCompatibility;
+    @NonNull
+    private final String mLanguageTag;
     private final String mNotificationTargetClassName;
     private final String mNotificationTargetPackageName;
     private final int mHashCode;
@@ -130,14 +140,18 @@
         final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
         mSuggestions = Arrays.copyOf(suggestions, N);
         mFlags = flags;
+        final Locale sourceLocale;
         if (locale != null) {
-            mLocaleString = locale.toString();
+            sourceLocale = locale;
         } else if (context != null) {
-            mLocaleString = context.getResources().getConfiguration().locale.toString();
+            // TODO: Consider to context.getResources().getResolvedLocale() instead.
+            sourceLocale = context.getResources().getConfiguration().locale;
         } else {
             Log.e("SuggestionSpan", "No locale or context specified in SuggestionSpan constructor");
-            mLocaleString = "";
+            sourceLocale = null;
         }
+        mLocaleStringForCompatibility = sourceLocale == null ? "" : sourceLocale.toString();
+        mLanguageTag = sourceLocale == null ? "" : sourceLocale.toLanguageTag();
 
         if (context != null) {
             mNotificationTargetPackageName = context.getPackageName();
@@ -150,7 +164,8 @@
         } else {
             mNotificationTargetClassName = "";
         }
-        mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
+        mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility,
+                mNotificationTargetClassName);
 
         initStyle(context);
     }
@@ -194,7 +209,8 @@
     public SuggestionSpan(Parcel src) {
         mSuggestions = src.readStringArray();
         mFlags = src.readInt();
-        mLocaleString = src.readString();
+        mLocaleStringForCompatibility = src.readString();
+        mLanguageTag = src.readString();
         mNotificationTargetClassName = src.readString();
         mNotificationTargetPackageName = src.readString();
         mHashCode = src.readInt();
@@ -214,10 +230,29 @@
     }
 
     /**
-     * @return the locale of the suggestions
+     * @deprecated use {@link #getLocaleObject()} instead.
+     * @return the locale of the suggestions. An empty string is returned if no locale is specified.
      */
+    @NonNull
+    @Deprecated
     public String getLocale() {
-        return mLocaleString;
+        return mLocaleStringForCompatibility;
+    }
+
+    /**
+     * Returns a well-formed BCP 47 language tag representation of the suggestions, as a
+     * {@link Locale} object.
+     *
+     * <p><b>Caveat</b>: The returned object is guaranteed to be a  a well-formed BCP 47 language tag
+     * representation.  For example, this method can return an empty locale rather than returning a
+     * malformed data when this object is initialized with an malformed {@link Locale} object, e.g.
+     * {@code new Locale(" a ", " b c d ", " "}.</p>
+     *
+     * @return the locale of the suggestions. {@code null} is returned if no locale is specified.
+     */
+    @Nullable
+    public Locale getLocaleObject() {
+        return mLanguageTag.isEmpty() ? null : Locale.forLanguageTag(mLanguageTag);
     }
 
     /**
@@ -255,7 +290,8 @@
     public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeStringArray(mSuggestions);
         dest.writeInt(mFlags);
-        dest.writeString(mLocaleString);
+        dest.writeString(mLocaleStringForCompatibility);
+        dest.writeString(mLanguageTag);
         dest.writeString(mNotificationTargetClassName);
         dest.writeString(mNotificationTargetPackageName);
         dest.writeInt(mHashCode);
@@ -290,10 +326,10 @@
         return mHashCode;
     }
 
-    private static int hashCodeInternal(String[] suggestions, String locale,
-            String notificationTargetClassName) {
+    private static int hashCodeInternal(String[] suggestions, @NonNull String languageTag,
+            @NonNull String localeStringForCompatibility, String notificationTargetClassName) {
         return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
-                locale, notificationTargetClassName});
+                languageTag, localeStringForCompatibility, notificationTargetClassName});
     }
 
     public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index c119277..fbd9924 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -218,7 +218,7 @@
         ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
 
         if ((mask & WEB_URLS) != 0) {
-            gatherLinks(links, text, Patterns.WEB_URL,
+            gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,
                 new String[] { "http://", "https://", "rtsp://" },
                 sUrlMatchFilter, null);
         }
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index b10f6a0..817541c 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -16,6 +16,7 @@
 package android.transition;
 
 import android.graphics.Rect;
+import android.transition.Slide.GravityFlag;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,7 +46,7 @@
      *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
      *             {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
      */
-    public void setSide(int side) {
+    public void setSide(@GravityFlag int side) {
         mSide = side;
     }
 
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9063b43..9af65e4 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -17,6 +17,7 @@
 
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -27,6 +28,9 @@
 import android.view.animation.DecelerateInterpolator;
 import com.android.internal.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes and moves views in or out from one of the edges of the
@@ -42,7 +46,12 @@
     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
     private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
     private CalculateSlide mSlideCalculator = sCalculateBottom;
-    private int mSlideEdge = Gravity.BOTTOM;
+    private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Gravity.LEFT, Gravity.TOP, Gravity.RIGHT, Gravity.BOTTOM, Gravity.START, Gravity.END})
+    public @interface GravityFlag {}
 
     private interface CalculateSlide {
 
@@ -176,7 +185,7 @@
      *                  {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
      * @attr ref android.R.styleable#Slide_slideEdge
      */
-    public void setSlideEdge(int slideEdge) {
+    public void setSlideEdge(@GravityFlag int slideEdge) {
         switch (slideEdge) {
             case Gravity.LEFT:
                 mSlideCalculator = sCalculateLeft;
@@ -214,6 +223,7 @@
      *         {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
      * @attr ref android.R.styleable#Slide_slideEdge
      */
+    @GravityFlag
     public int getSlideEdge() {
         return mSlideEdge;
     }
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index e711812..eb95a02 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -16,17 +16,21 @@
 
 package android.transition;
 
-import com.android.internal.R;
-
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.Animator.AnimatorPauseListener;
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes. Visibility is determined not just by the
@@ -46,6 +50,11 @@
     private static final String PROPNAME_PARENT = "android:visibility:parent";
     private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag=true, value={MODE_IN, MODE_OUT})
+    @interface VisibilityMode {}
+
     /**
      * Mode used in {@link #setMode(int)} to make the transition
      * operate on targets that are appearing. Maybe be combined with
@@ -99,7 +108,7 @@
      *             {@link #MODE_IN} and {@link #MODE_OUT}.
      * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
      */
-    public void setMode(int mode) {
+    public void setMode(@VisibilityMode int mode) {
         if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
             throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
         }
@@ -113,6 +122,7 @@
      *         {@link #MODE_IN} and {@link #MODE_OUT}.
      * @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
      */
+    @VisibilityMode
     public int getMode() {
         return mMode;
     }
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index 1becfb4..24883e3 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -16,6 +16,7 @@
 
 package android.util;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
@@ -28,12 +29,6 @@
 import java.util.HashSet;
 import java.util.Locale;
 
-// TODO: We don't except too many LocaleLists to exist at the same time, and
-// we need access to the data at native level, so we should pass the data
-// down to the native level, create a map of every list seen there, take a
-// pointer back, and just keep that pointer in the Java-level object, so
-// things could be copied very quickly.
-
 /**
  * LocaleList is an immutable list of Locales, typically used to keep an
  * ordered user preferences for locales.
@@ -62,10 +57,21 @@
         return mList.length == 0;
     }
 
+    @IntRange(from=0)
     public int size() {
         return mList.length;
     }
 
+    @IntRange(from=-1)
+    public int indexOf(Locale locale) {
+        for (int i = 0; i < mList.length; i++) {
+            if (mList[i].equals(locale)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (other == this)
@@ -75,7 +81,7 @@
         final Locale[] otherList = ((LocaleList) other).mList;
         if (mList.length != otherList.length)
             return false;
-        for (int i = 0; i < mList.length; ++i) {
+        for (int i = 0; i < mList.length; i++) {
             if (!mList[i].equals(otherList[i]))
                 return false;
         }
@@ -85,7 +91,7 @@
     @Override
     public int hashCode() {
         int result = 1;
-        for (int i = 0; i < mList.length; ++i) {
+        for (int i = 0; i < mList.length; i++) {
             result = 31 * result + mList[i].hashCode();
         }
         return result;
@@ -95,7 +101,7 @@
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("[");
-        for (int i = 0; i < mList.length; ++i) {
+        for (int i = 0; i < mList.length; i++) {
             sb.append(mList[i]);
             if (i < mList.length - 1) {
                 sb.append(',');
@@ -156,12 +162,12 @@
             final Locale[] localeList = new Locale[list.length];
             final HashSet<Locale> seenLocales = new HashSet<Locale>();
             final StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < list.length; ++i) {
+            for (int i = 0; i < list.length; i++) {
                 final Locale l = list[i];
                 if (l == null) {
-                    throw new NullPointerException();
+                    throw new NullPointerException("list[" + i + "] is null");
                 } else if (seenLocales.contains(l)) {
-                    throw new IllegalArgumentException();
+                    throw new IllegalArgumentException("list[" + i + "] is a repetition");
                 } else {
                     final Locale localeClone = (Locale) l.clone();
                     localeList[i] = localeClone;
@@ -177,6 +183,55 @@
         }
     }
 
+    /**
+     * Constructs a locale list, with the topLocale moved to the front if it already is
+     * in otherLocales, or added to the front if it isn't.
+     *
+     * {@hide}
+     */
+    public LocaleList(@NonNull Locale topLocale, LocaleList otherLocales) {
+        if (topLocale == null) {
+            throw new NullPointerException("topLocale is null");
+        }
+
+        final int inputLength = (otherLocales == null) ? 0 : otherLocales.mList.length;
+        int topLocaleIndex = -1;
+        for (int i = 0; i < inputLength; i++) {
+            if (topLocale.equals(otherLocales.mList[i])) {
+                topLocaleIndex = i;
+                break;
+            }
+        }
+
+        final int outputLength = inputLength + (topLocaleIndex == -1 ? 1 : 0);
+        final Locale[] localeList = new Locale[outputLength];
+        localeList[0] = (Locale) topLocale.clone();
+        if (topLocaleIndex == -1) {
+            // topLocale was not in otherLocales
+            for (int i = 0; i < inputLength; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+        } else {
+            for (int i = 0; i < topLocaleIndex; i++) {
+                localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
+            }
+            for (int i = topLocaleIndex + 1; i < inputLength; i++) {
+                localeList[i] = (Locale) otherLocales.mList[i].clone();
+            }
+        }
+
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < outputLength; i++) {
+            sb.append(localeList[i].toLanguageTag());
+            if (i < outputLength - 1) {
+                sb.append(',');
+            }
+        }
+
+        mList = localeList;
+        mStringRepresentation = sb.toString();
+    }
+
     public static final Parcelable.Creator<LocaleList> CREATOR
             = new Parcelable.Creator<LocaleList>() {
         @Override
@@ -202,7 +257,7 @@
         } else {
             final String[] tags = list.split(",");
             final Locale[] localeArray = new Locale[tags.length];
-            for (int i = 0; i < localeArray.length; ++i) {
+            for (int i = 0; i < localeArray.length; i++) {
                 localeArray[i] = Locale.forLanguageTag(tags[i]);
             }
             return new LocaleList(localeArray);
@@ -219,6 +274,21 @@
         }
     }
 
+    private static final String STRING_EN_XA = "en-XA";
+    private static final String STRING_AR_XB = "ar-XB";
+    private static final Locale LOCALE_EN_XA = new Locale("en", "XA");
+    private static final Locale LOCALE_AR_XB = new Locale("ar", "XB");
+    private static final int NUM_PSEUDO_LOCALES = 2;
+
+    private static boolean isPseudoLocale(String locale) {
+        return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale);
+    }
+
+    private static boolean isPseudoLocale(Locale locale) {
+        return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
+    }
+
+    @IntRange(from=0, to=1)
     private static int matchScore(Locale supported, Locale desired) {
         if (supported.equals(desired)) {
             return 1;  // return early so we don't do unnecessary computation
@@ -226,6 +296,11 @@
         if (!supported.getLanguage().equals(desired.getLanguage())) {
             return 0;
         }
+        if (isPseudoLocale(supported) || isPseudoLocale(desired)) {
+            // The locales are not the same, but the languages are the same, and one of the locales
+            // is a pseudo-locale. So this is not a match.
+            return 0;
+        }
         // There is no match if the two locales use different scripts. This will most imporantly
         // take care of traditional vs simplified Chinese.
         final String supportedScr = getLikelyScript(supported);
@@ -233,24 +308,26 @@
         return supportedScr.equals(desiredScr) ? 1 : 0;
     }
 
-    /**
-     * Returns the first match in the locale list given an unordered array of supported locales
-     * in BCP47 format.
-     *
-     * If the locale list is empty, null would be returned.
-     */
-    @Nullable
-    public Locale getFirstMatch(String[] supportedLocales) {
+    private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn");
+
+    private Locale computeFirstMatch(String[] supportedLocales, boolean assumeEnglishIsSupported) {
         if (mList.length == 1) {  // just one locale, perhaps the most common scenario
             return mList[0];
         }
         if (mList.length == 0) {  // empty locale list
             return null;
         }
-        // TODO: Figure out what to if en-XA or ar-XB are in the locale list
         int bestIndex = Integer.MAX_VALUE;
-        for (String tag : supportedLocales) {
-            final Locale supportedLocale = Locale.forLanguageTag(tag);
+        final int numSupportedLocales =
+                supportedLocales.length + (assumeEnglishIsSupported ? 1 : 0);
+        for (int i = 0; i < numSupportedLocales; i++) {
+            final Locale supportedLocale;
+            if (assumeEnglishIsSupported) {
+                // Try English first, so we can return early if it's in the LocaleList
+                supportedLocale = (i == 0) ? EN_LATN : Locale.forLanguageTag(supportedLocales[i-1]);
+            } else {
+                supportedLocale = Locale.forLanguageTag(supportedLocales[i]);
+            }
             // We expect the average length of locale lists used for locale resolution to be
             // smaller than three, so it's OK to do this as an O(mn) algorithm.
             for (int idx = 0; idx < mList.length; idx++) {
@@ -271,21 +348,123 @@
         }
     }
 
+    /**
+     * Returns the first match in the locale list given an unordered array of supported locales
+     * in BCP47 format.
+     *
+     * If the locale list is empty, null would be returned.
+     */
+    @Nullable
+    public Locale getFirstMatch(String[] supportedLocales) {
+        return computeFirstMatch(supportedLocales, false /* assume English is not supported */);
+    }
+
+    /**
+     * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
+     * {@hide}
+     */
+    @Nullable
+    public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) {
+        return computeFirstMatch(supportedLocales, true /* assume English is supported */);
+    }
+
+    /**
+     * Returns true if the array of locale tags only contains empty locales and pseudolocales.
+     * Assumes that there is no repetition in the input.
+     * {@hide}
+     */
+    public static boolean isPseudoLocalesOnly(String[] supportedLocales) {
+        if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
+            // This is for optimization. Since there's no repetition in the input, if we have more
+            // than the number of pseudo-locales plus one for the empty string, it's guaranteed
+            // that we have some meaninful locale in the list, so the list is not "practically
+            // empty".
+            return false;
+        }
+        for (String locale : supportedLocales) {
+            if (!locale.isEmpty() && !isPseudoLocale(locale)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private final static Object sLock = new Object();
 
     @GuardedBy("sLock")
-    private static LocaleList sDefaultLocaleList;
+    private static LocaleList sLastExplicitlySetLocaleList = null;
+    @GuardedBy("sLock")
+    private static LocaleList sDefaultLocaleList = null;
+    @GuardedBy("sLock")
+    private static Locale sLastDefaultLocale = null;
 
-    // TODO: fix this to return the default system locale list once we have that
+    /**
+     * The result is guaranteed to include the default Locale returned by Locale.getDefault(), but
+     * not necessarily at the top of the list. The default locale not being at the top of the list
+     * is an indication that the system has set the default locale to one of the user's other
+     * preferred locales, having concluded that the primary preference is not supported but a
+     * secondary preference is.
+     *
+     * Note that the default LocaleList would change if Locale.setDefault() is called. This method
+     * takes that into account by always checking the output of Locale.getDefault() and adjusting
+     * the default LocaleList if needed.
+     */
     @NonNull @Size(min=1)
     public static LocaleList getDefault() {
-        Locale defaultLocale = Locale.getDefault();
+        final Locale defaultLocale = Locale.getDefault();
         synchronized (sLock) {
-            if (sDefaultLocaleList == null || sDefaultLocaleList.size() != 1
-                    || !defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
-                sDefaultLocaleList = new LocaleList(defaultLocale);
+            if (!defaultLocale.equals(sLastDefaultLocale)) {
+                sLastDefaultLocale = defaultLocale;
+                // It's either the first time someone has asked for the default locale list, or
+                // someone has called Locale.setDefault() since we last set or adjusted the default
+                // locale list. So let's adjust the locale list.
+                if (sDefaultLocaleList != null
+                        && defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
+                    // The default Locale has changed, but it happens to be the first locale in the
+                    // default locale list, so we don't need to construct a new locale list.
+                    return sDefaultLocaleList;
+                }
+                sDefaultLocaleList = new LocaleList(defaultLocale, sLastExplicitlySetLocaleList);
             }
+            // sDefaultLocaleList can't be null, since it can't be set to null by
+            // LocaleList.setDefault(), and if getDefault() is called before a call to
+            // setDefault(), sLastDefaultLocale would be null and the check above would set
+            // sDefaultLocaleList.
+            return sDefaultLocaleList;
         }
-        return sDefaultLocaleList;
+    }
+
+    /**
+     * Also sets the default locale by calling Locale.setDefault() with the first locale in the
+     * list.
+     *
+     * @throws NullPointerException if the input is <code>null</code>.
+     * @throws IllegalArgumentException if the input is empty.
+     */
+    public static void setDefault(@NonNull @Size(min=1) LocaleList locales) {
+        setDefault(locales, 0);
+    }
+
+    /**
+     * This may be used directly by system processes to set the default locale list for apps. For
+     * such uses, the default locale list would always come from the user preferences, but the
+     * default locale may have been chosen to be a locale other than the first locale in the locale
+     * list (based on the locales the app supports).
+     *
+     * {@hide}
+     */
+    public static void setDefault(@NonNull @Size(min=1) LocaleList locales, int localeIndex) {
+        if (locales == null) {
+            throw new NullPointerException("locales is null");
+        }
+        if (locales.isEmpty()) {
+            throw new IllegalArgumentException("locales is empty");
+        }
+        synchronized (sLock) {
+            sLastDefaultLocale = locales.get(localeIndex);
+            Locale.setDefault(sLastDefaultLocale);
+            sLastExplicitlySetLocaleList = locales;
+            sDefaultLocaleList = locales;
+        }
     }
 }
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 2cc91b9..9f2bcfd 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -109,11 +109,137 @@
         + "|z[amw]))";
 
     /**
-     * Good characters for Internationalized Resource Identifiers (IRI).
-     * This comprises most common used Unicode characters allowed in IRI
-     * as detailed in RFC 3987.
-     * Specifically, those two byte Unicode characters are not included.
+     *  Regular expression to match all IANA top-level domains.
+     *
+     *  List accurate as of 2015/11/24.  List taken from:
+     *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+     *  This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
+     *
+     *  @hide
      */
+    static final String IANA_TOP_LEVEL_DOMAINS =
+        "(?:"
+        + "(?:aaa|aarp|abb|abbott|abogado|academy|accenture|accountant|accountants|aco|active"
+        + "|actor|ads|adult|aeg|aero|afl|agency|aig|airforce|airtel|allfinanz|alsace|amica|amsterdam"
+        + "|android|apartments|app|apple|aquarelle|aramco|archi|army|arpa|arte|asia|associates"
+        + "|attorney|auction|audio|auto|autos|axa|azure|a[cdefgilmoqrstuwxz])"
+        + "|(?:band|bank|bar|barcelona|barclaycard|barclays|bargains|bauhaus|bayern|bbc|bbva"
+        + "|bcn|beats|beer|bentley|berlin|best|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
+        + "|blackfriday|bloomberg|blue|bms|bmw|bnl|bnpparibas|boats|bom|bond|boo|boots|boutique"
+        + "|bradesco|bridgestone|broadway|broker|brother|brussels|budapest|build|builders|business"
+        + "|buzz|bzh|b[abdefghijmnorstvwyz])"
+        + "|(?:cab|cafe|cal|camera|camp|cancerresearch|canon|capetown|capital|car|caravan|cards"
+        + "|care|career|careers|cars|cartier|casa|cash|casino|cat|catering|cba|cbn|ceb|center|ceo"
+        + "|cern|cfa|cfd|chanel|channel|chat|cheap|chloe|christmas|chrome|church|cipriani|cisco"
+        + "|citic|city|cityeats|claims|cleaning|click|clinic|clothing|cloud|club|clubmed|coach"
+        + "|codes|coffee|college|cologne|com|commbank|community|company|computer|comsec|condos"
+        + "|construction|consulting|contractors|cooking|cool|coop|corsica|country|coupons|courses"
+        + "|credit|creditcard|creditunion|cricket|crown|crs|cruises|csc|cuisinella|cymru|cyou|c[acdfghiklmnoruvwxyz])"
+        + "|(?:dabur|dad|dance|date|dating|datsun|day|dclk|deals|degree|delivery|dell|delta"
+        + "|democrat|dental|dentist|desi|design|dev|diamonds|diet|digital|direct|directory|discount"
+        + "|dnp|docs|dog|doha|domains|doosan|download|drive|durban|dvag|d[ejkmoz])"
+        + "|(?:earth|eat|edu|education|email|emerck|energy|engineer|engineering|enterprises"
+        + "|epson|equipment|erni|esq|estate|eurovision|eus|events|everbank|exchange|expert|exposed"
+        + "|express|e[cegrstu])"
+        + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|fashion|feedback|ferrero|film"
+        + "|final|finance|financial|firmdale|fish|fishing|fit|fitness|flights|florist|flowers|flsmidth"
+        + "|fly|foo|football|forex|forsale|forum|foundation|frl|frogans|fund|furniture|futbol|fyi"
+        + "|f[ijkmor])"
+        + "|(?:gal|gallery|game|garden|gbiz|gdn|gea|gent|genting|ggee|gift|gifts|gives|giving"
+        + "|glass|gle|global|globo|gmail|gmo|gmx|gold|goldpoint|golf|goo|goog|google|gop|gov|grainger"
+        + "|graphics|gratis|green|gripe|group|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
+        + "|(?:hamburg|hangout|haus|healthcare|help|here|hermes|hiphop|hitachi|hiv|hockey|holdings"
+        + "|holiday|homedepot|homes|honda|horse|host|hosting|hoteles|hotmail|house|how|hsbc|hyundai"
+        + "|h[kmnrtu])"
+        + "|(?:ibm|icbc|ice|icu|ifm|iinet|immo|immobilien|industries|infiniti|info|ing|ink|institute"
+        + "|insure|int|international|investments|ipiranga|irish|ist|istanbul|itau|iwc|i[delmnoqrst])"
+        + "|(?:jaguar|java|jcb|jetzt|jewelry|jlc|jll|jobs|joburg|jprs|juegos|j[emop])"
+        + "|(?:kaufen|kddi|kia|kim|kinder|kitchen|kiwi|koeln|komatsu|krd|kred|kyoto|k[eghimnprwyz])"
+        + "|(?:lacaixa|lancaster|land|landrover|lasalle|lat|latrobe|law|lawyer|lds|lease|leclerc"
+        + "|legal|lexus|lgbt|liaison|lidl|life|lifestyle|lighting|limited|limo|linde|link|live"
+        + "|lixil|loan|loans|lol|london|lotte|lotto|love|ltd|ltda|lupin|luxe|luxury|l[abcikrstuvy])"
+        + "|(?:madrid|maif|maison|man|management|mango|market|marketing|markets|marriott|mba"
+        + "|media|meet|melbourne|meme|memorial|men|menu|meo|miami|microsoft|mil|mini|mma|mobi|moda"
+        + "|moe|moi|mom|monash|money|montblanc|mormon|mortgage|moscow|motorcycles|mov|movie|movistar"
+        + "|mtn|mtpc|mtr|museum|mutuelle|m[acdeghklmnopqrstuvwxyz])"
+        + "|(?:nadex|nagoya|name|navy|nec|net|netbank|network|neustar|new|news|nexus|ngo|nhk"
+        + "|nico|ninja|nissan|nokia|nra|nrw|ntt|nyc|n[acefgilopruz])"
+        + "|(?:obi|office|okinawa|omega|one|ong|onl|online|ooo|oracle|orange|org|organic|osaka"
+        + "|otsuka|ovh|om)"
+        + "|(?:page|panerai|paris|partners|parts|party|pet|pharmacy|philips|photo|photography"
+        + "|photos|physio|piaget|pics|pictet|pictures|ping|pink|pizza|place|play|playstation|plumbing"
+        + "|plus|pohl|poker|porn|post|praxi|press|pro|prod|productions|prof|properties|property"
+        + "|protection|pub|p[aefghklmnrstwy])"
+        + "|(?:qpon|quebec|qa)"
+        + "|(?:racing|realtor|realty|recipes|red|redstone|rehab|reise|reisen|reit|ren|rent|rentals"
+        + "|repair|report|republican|rest|restaurant|review|reviews|rich|ricoh|rio|rip|rocher|rocks"
+        + "|rodeo|rsvp|ruhr|run|rwe|ryukyu|r[eosuw])"
+        + "|(?:saarland|sakura|sale|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|saxo"
+        + "|sbs|sca|scb|schmidt|scholarships|school|schule|schwarz|science|scor|scot|seat|security"
+        + "|seek|sener|services|seven|sew|sex|sexy|shiksha|shoes|show|shriram|singles|site|ski"
+        + "|sky|skype|sncf|soccer|social|software|sohu|solar|solutions|sony|soy|space|spiegel|spreadbetting"
+        + "|srl|stada|starhub|statoil|stc|stcgroup|stockholm|studio|study|style|sucks|supplies"
+        + "|supply|support|surf|surgery|suzuki|swatch|swiss|sydney|systems|s[abcdeghijklmnortuvxyz])"
+        + "|(?:tab|taipei|tatamotors|tatar|tattoo|tax|taxi|team|tech|technology|tel|telefonica"
+        + "|temasek|tennis|thd|theater|theatre|tickets|tienda|tips|tires|tirol|today|tokyo|tools"
+        + "|top|toray|toshiba|tours|town|toyota|toys|trade|trading|training|travel|trust|tui|t[cdfghjklmnortvwz])"
+        + "|(?:ubs|university|uno|uol|u[agksyz])"
+        + "|(?:vacations|vana|vegas|ventures|versicherung|vet|viajes|video|villas|vin|virgin"
+        + "|vision|vista|vistaprint|viva|vlaanderen|vodka|vote|voting|voto|voyage|v[aceginu])"
+        + "|(?:wales|walter|wang|watch|webcam|website|wed|wedding|weir|whoswho|wien|wiki|williamhill"
+        + "|win|windows|wine|wme|work|works|world|wtc|wtf|w[fs])"
+        + "|(?:\u03b5\u03bb|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438|\u043a\u043e\u043c|\u043c\u043a\u0434"
+        + "|\u043c\u043e\u043d|\u043c\u043e\u0441\u043a\u0432\u0430|\u043e\u043d\u043b\u0430\u0439\u043d"
+        + "|\u043e\u0440\u0433|\u0440\u0443\u0441|\u0440\u0444|\u0441\u0430\u0439\u0442|\u0441\u0440\u0431"
+        + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05e7\u05d5\u05dd|\u0627\u0631\u0627\u0645\u0643\u0648"
+        + "|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
+        + "|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0627\u06cc\u0631\u0627\u0646"
+        + "|\u0628\u0627\u0632\u0627\u0631|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633"
+        + "|\u0633\u0648\u062f\u0627\u0646|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629"
+        + "|\u0639\u0631\u0627\u0642|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646"
+        + "|\u0642\u0637\u0631|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627"
+        + "|\u0645\u0648\u0642\u0639|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
+        + "|\u0938\u0902\u0917\u0920\u0928|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4"
+        + "|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
+        + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22"
+        + "|\u10d2\u10d4|\u307f\u3093\u306a|\u30b0\u30fc\u30b0\u30eb|\u30b3\u30e0|\u4e16\u754c"
+        + "|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51|\u4f01\u4e1a|\u4f5b\u5c71"
+        + "|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063"
+        + "|\u5546\u57ce|\u5546\u5e97|\u5546\u6807|\u5728\u7ebf|\u5927\u62ff|\u5a31\u4e50|\u5de5\u884c"
+        + "|\u5e7f\u4e1c|\u6148\u5584|\u6211\u7231\u4f60|\u624b\u673a|\u653f\u52a1|\u653f\u5e9c"
+        + "|\u65b0\u52a0\u5761|\u65b0\u95fb|\u65f6\u5c1a|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f"
+        + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7edc"
+        + "|\u8c37\u6b4c|\u96c6\u56e2|\u98de\u5229\u6d66|\u9910\u5385|\u9999\u6e2f|\ub2f7\ub137"
+        + "|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d|xbox"
+        + "|xerox|xin|xn\\-\\-11b4c3d|xn\\-\\-1qqw23a|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g"
+        + "|xn\\-\\-3e0b707e|xn\\-\\-3pxu8k|xn\\-\\-42c2d9a|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4gbrim"
+        + "|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks"
+        + "|xn\\-\\-80ao21a|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-90a3ac|xn\\-\\-90ais|xn\\-\\-9dbq2a"
+        + "|xn\\-\\-9et52u|xn\\-\\-b4w605ferd|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
+        + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-efvy88h"
+        + "|xn\\-\\-estv75g|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s"
+        + "|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-gecrj9c"
+        + "|xn\\-\\-h2brj9c|xn\\-\\-hxt814e|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i"
+        + "|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
+        + "|xn\\-\\-kput3i|xn\\-\\-l1acc|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgb9awbf|xn\\-\\-mgba3a3ejt"
+        + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e"
+        + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbpl2fh|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
+        + "|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema"
+        + "|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh"
+        + "|xn\\-\\-pssy2u|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-s9brj9c"
+        + "|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb"
+        + "|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv|xn\\-\\-vuq861b|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
+        + "|xn\\-\\-xhq521b|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-y9a3aq|xn\\-\\-yfro4i67o"
+        + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xperia|xxx|xyz)"
+        + "|(?:yachts|yamaxun|yandex|yodobashi|yoga|yokohama|youtube|y[et])"
+        + "|(?:zara|zip|zone|zuerich|z[amw]))";
+
+    /**
+     * Kept for backward compatibility reasons.
+     *
+     * @deprecated Deprecated since it does not include all IRI characters defined in RFC 3987
+     */
+    @Deprecated
     public static final String GOOD_IRI_CHAR =
         "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
 
@@ -125,35 +251,148 @@
             + "|[1-9][0-9]|[0-9]))");
 
     /**
+     * Valid UCS characters defined in RFC 3987.
+     */
+    private static final String UCS_CHAR =
+            "\u00A0-\uD7FF" +
+            "\uF900-\uFDCF" +
+            "\uFDF0-\uFFEF" +
+            "\uD800\uDC00-\uD83F\uDFFD" +
+            "\uD840\uDC00-\uD87F\uDFFD" +
+            "\uD880\uDC00-\uD8BF\uDFFD" +
+            "\uD8C0\uDC00-\uD8FF\uDFFD" +
+            "\uD900\uDC00-\uD93F\uDFFD" +
+            "\uD940\uDC00-\uD97F\uDFFD" +
+            "\uD980\uDC00-\uD9BF\uDFFD" +
+            "\uD9C0\uDC00-\uD9FF\uDFFD" +
+            "\uDA00\uDC00-\uDA3F\uDFFD" +
+            "\uDA40\uDC00-\uDA7F\uDFFD" +
+            "\uDA80\uDC00-\uDABF\uDFFD" +
+            "\uDAC0\uDC00-\uDAFF\uDFFD" +
+            "\uDB00\uDC00-\uDB3F\uDFFD" +
+            "\uDB44\uDC00-\uDB7F\uDFFD";
+
+    /**
+     * Valid characters for IRI label defined in RFC 3987.
+     */
+    private static final String LABEL_CHAR = "a-zA-Z0-9" + UCS_CHAR;
+
+    /**
+     * Valid characters for IRI TLD defined in RFC 3987.
+     */
+    private static final String TLD_CHAR = "a-zA-Z" + UCS_CHAR;
+
+    /**
      * RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets.
      */
-    private static final String IRI
-        = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}";
+    private static final String IRI_LABEL =
+            "[" + LABEL_CHAR + "](?:[" + LABEL_CHAR + "\\-]{0,61}[" + LABEL_CHAR + "]){0,1}";
 
-    private static final String GOOD_GTLD_CHAR =
-        "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
-    private static final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}";
-    private static final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD;
+    /**
+     * RFC 3492 references RFC 1034 and limits Punycode algorithm output to 63 characters.
+     */
+    private static final String PUNYCODE_TLD = "xn\\-\\-[\\w\\-]{0,58}\\w";
+
+    private static final String TLD = "(" + PUNYCODE_TLD + "|" + "[" + TLD_CHAR + "]{2,63}" +")";
+
+    private static final String HOST_NAME = "(" + IRI_LABEL + "\\.)+" + TLD;
 
     public static final Pattern DOMAIN_NAME
         = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
 
+    private static final String PROTOCOL = "(?i:http|https|rtsp):\\/\\/";
+
+    /* A word boundary or end of input.  This is to stop foo.sure from matching as foo.su */
+    private static final String WORD_BOUNDARY = "(?:\\b|$|^)";
+
+    private static final String USER_INFO = "(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+            + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+            + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@";
+
+    private static final String PORT_NUMBER = "\\:\\d{1,5}";
+
+    private static final String PATH_AND_QUERY = "\\/(?:(?:[" + LABEL_CHAR
+            + "\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus optional query params
+            + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*";
+
     /**
      *  Regular expression pattern to match most part of RFC 3987
-     *  Internationalized URLs, aka IRIs.  Commonly used Unicode characters are
-     *  added.
+     *  Internationalized URLs, aka IRIs.
      */
-    public static final Pattern WEB_URL = Pattern.compile(
-        "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
-        + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
-        + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
-        + "(?:" + DOMAIN_NAME + ")"
-        + "(?:\\:\\d{1,5})?)" // plus option port number
-        + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~"  // plus option query params
-        + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
-        + "(?:\\b|$)"); // and finally, a word boundary or end of
-                        // input.  This is to stop foo.sure from
-                        // matching as foo.su
+    public static final Pattern WEB_URL = Pattern.compile("("
+            + "("
+            + "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?"
+            + "(?:" + DOMAIN_NAME + ")"
+            + "(?:" + PORT_NUMBER + ")?"
+            + ")"
+            + "(" + PATH_AND_QUERY + ")?"
+            + WORD_BOUNDARY
+            + ")");
+
+    /**
+     * Regular expression that matches known TLDs and punycode TLDs
+     */
+    private static final String STRICT_TLD = "(?:" +
+            IANA_TOP_LEVEL_DOMAINS + "|" + PUNYCODE_TLD + ")";
+
+    /**
+     * Regular expression that matches host names using {@link #STRICT_TLD}
+     */
+    private static final String STRICT_HOST_NAME = "(?:(?:" + IRI_LABEL + "\\.)+"
+            + STRICT_TLD + ")";
+
+    /**
+     * Regular expression that matches domain names using either {@link #STRICT_HOST_NAME} or
+     * {@link #IP_ADDRESS}
+     */
+    private static final Pattern STRICT_DOMAIN_NAME
+            = Pattern.compile("(?:" + STRICT_HOST_NAME + "|" + IP_ADDRESS + ")");
+
+    /**
+     * Regular expression that matches domain names without a TLD
+     */
+    private static final String RELAXED_DOMAIN_NAME =
+            "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS + ")";
+
+    /**
+     * Regular expression to match strings that do not start with a supported protocol. The TLDs
+     * are expected to be one of the known TLDs.
+     */
+    private static final String WEB_URL_WITHOUT_PROTOCOL = "("
+            + WORD_BOUNDARY
+            + "(?<!:\\/\\/)"
+            + "("
+            + "(?:" + STRICT_DOMAIN_NAME + ")"
+            + "(?:" + PORT_NUMBER + ")?"
+            + ")"
+            + "(?:" + PATH_AND_QUERY + ")?"
+            + WORD_BOUNDARY
+            + ")";
+
+    /**
+     * Regular expression to match strings that start with a supported protocol. Rules for domain
+     * names and TLDs are more relaxed. TLDs are optional.
+     */
+    private static final String WEB_URL_WITH_PROTOCOL = "("
+            + WORD_BOUNDARY
+            + "(?:"
+            + "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")"
+            + "(?:" + RELAXED_DOMAIN_NAME + ")?"
+            + "(?:" + PORT_NUMBER + ")?"
+            + ")"
+            + "(?:" + PATH_AND_QUERY + ")?"
+            + WORD_BOUNDARY
+            + ")";
+
+    /**
+     * Regular expression pattern to match IRIs. If a string starts with http(s):// the expression
+     * tries to match the URL structure with a relaxed rule for TLDs. If the string does not start
+     * with http(s):// the TLDs are expected to be one of the known TLDs.
+     *
+     * @hide
+     */
+    public static final Pattern AUTOLINK_WEB_URL = Pattern.compile(
+            "(" + WEB_URL_WITH_PROTOCOL + "|" + WEB_URL_WITHOUT_PROTOCOL + ")");
 
     public static final Pattern EMAIL_ADDRESS
         = Pattern.compile(
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
new file mode 100644
index 0000000..81c8c45
--- /dev/null
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -0,0 +1,1033 @@
+/*
+ * 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.util.apk;
+
+import android.util.Pair;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.DigestException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * APK Signature Scheme v2 verifier.
+ *
+ * @hide for internal use only.
+ */
+public class ApkSignatureSchemeV2Verifier {
+
+    /**
+     * {@code .SF} file header section attribute indicating that the APK is signed not just with
+     * JAR signature scheme but also with APK Signature Scheme v2 or newer. This attribute
+     * facilitates v2 signature stripping detection.
+     *
+     * <p>The attribute contains a comma-separated set of signature scheme IDs.
+     */
+    public static final String SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME = "X-Android-APK-Signed";
+    // TODO: Change the value when signing scheme finalized.
+    public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 1234567890;
+
+    /**
+     * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
+     * associated with each signer.
+     *
+     * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
+     * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+     * @throws IOException if an I/O error occurs while reading the APK file.
+     */
+    public static X509Certificate[][] verify(String apkFile)
+            throws SignatureNotFoundException, SecurityException, IOException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
+            return verify(apk);
+        }
+    }
+
+    /**
+     * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
+     * associated with each signer.
+     *
+     * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
+     * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+     * @throws IOException if an I/O error occurs while reading the APK file.
+     */
+    public static X509Certificate[][] verify(RandomAccessFile apk)
+            throws SignatureNotFoundException, SecurityException, IOException {
+
+        long fileSize = apk.length();
+        if (fileSize > Integer.MAX_VALUE) {
+            throw new IOException("File too large: " + apk.length() + " bytes");
+        }
+        MappedByteBuffer apkContents =
+                apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+        // Attempt to preload the contents into memory for faster overall verification (v2 and
+        // older) at the expense of somewhat increased latency for rejecting malformed APKs.
+        apkContents.load();
+        return verify(apkContents);
+    }
+
+    /**
+     * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
+     * associated with each signer.
+     *
+     * @param apkContents contents of the APK. The contents start at the current position and end
+     *        at the limit of the buffer.
+     *
+     * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
+     * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+     */
+    public static X509Certificate[][] verify(ByteBuffer apkContents)
+            throws SignatureNotFoundException, SecurityException {
+        // Avoid modifying byte order, position, limit, and mark of the original apkContents.
+        apkContents = apkContents.slice();
+
+        // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
+        apkContents.order(ByteOrder.LITTLE_ENDIAN);
+
+        // Find the offset of ZIP End of Central Directory (EoCD)
+        int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents);
+        if (eocdOffset == -1) {
+            throw new SignatureNotFoundException(
+                    "Not an APK file: ZIP End of Central Directory record not found");
+        }
+        if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) {
+            throw new SignatureNotFoundException("ZIP64 APK not supported");
+        }
+        ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity());
+
+        // Look up the offset of ZIP Central Directory.
+        long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
+        if (centralDirOffsetLong >= eocdOffset) {
+            throw new SignatureNotFoundException(
+                    "ZIP Central Directory offset out of range: " + centralDirOffsetLong
+                    + ". ZIP End of Central Directory offset: " + eocdOffset);
+        }
+        long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
+        if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) {
+            throw new SignatureNotFoundException(
+                    "ZIP Central Directory is not immediately followed by End of Central"
+                    + " Directory");
+        }
+        int centralDirOffset = (int) centralDirOffsetLong;
+
+        // Find the APK Signing Block.
+        int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
+        ByteBuffer apkSigningBlock =
+                sliceFromTo(apkContents, apkSigningBlockOffset, centralDirOffset);
+
+        // Find the APK Signature Scheme v2 Block inside the APK Signing Block.
+        ByteBuffer apkSignatureSchemeV2Block = findApkSignatureSchemeV2Block(apkSigningBlock);
+
+        // Verify the contents of the APK outside of the APK Signing Block using the APK Signature
+        // Scheme v2 Block.
+        return verify(
+                apkContents,
+                apkSignatureSchemeV2Block,
+                apkSigningBlockOffset,
+                centralDirOffset,
+                eocdOffset);
+    }
+
+    /**
+     * Verifies the contents outside of the APK Signing Block using the provided APK Signature
+     * Scheme v2 Block.
+     */
+    private static X509Certificate[][] verify(
+            ByteBuffer apkContents,
+            ByteBuffer v2Block,
+            int apkSigningBlockOffset,
+            int centralDirOffset,
+            int eocdOffset) throws SecurityException {
+        int signerCount = 0;
+        Map<Integer, byte[]> contentDigests = new HashMap<>();
+        List<X509Certificate[]> signerCerts = new ArrayList<>();
+        CertificateFactory certFactory;
+        try {
+            certFactory = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+        }
+        ByteBuffer signers;
+        try {
+            signers = getLengthPrefixedSlice(v2Block);
+        } catch (IOException e) {
+            throw new SecurityException("Failed to read list of signers", e);
+        }
+        while (signers.hasRemaining()) {
+            signerCount++;
+            try {
+                ByteBuffer signer = getLengthPrefixedSlice(signers);
+                X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory);
+                signerCerts.add(certs);
+            } catch (IOException | BufferUnderflowException | SecurityException e) {
+                throw new SecurityException(
+                        "Failed to parse/verify signer #" + signerCount + " block",
+                        e);
+            }
+        }
+
+        if (signerCount < 1) {
+            throw new SecurityException("No signers found");
+        }
+
+        if (contentDigests.isEmpty()) {
+            throw new SecurityException("No content digests found");
+        }
+
+        verifyIntegrity(
+                contentDigests,
+                apkContents,
+                apkSigningBlockOffset,
+                centralDirOffset,
+                eocdOffset);
+
+        return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
+    }
+
+    private static X509Certificate[] verifySigner(
+            ByteBuffer signerBlock,
+            Map<Integer, byte[]> contentDigests,
+            CertificateFactory certFactory) throws SecurityException, IOException {
+        ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
+        ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
+        byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
+
+        int signatureCount = 0;
+        int bestSigAlgorithm = -1;
+        byte[] bestSigAlgorithmSignatureBytes = null;
+        List<Integer> signaturesSigAlgorithms = new ArrayList<>();
+        while (signatures.hasRemaining()) {
+            signatureCount++;
+            try {
+                ByteBuffer signature = getLengthPrefixedSlice(signatures);
+                if (signature.remaining() < 8) {
+                    throw new SecurityException("Signature record too short");
+                }
+                int sigAlgorithm = signature.getInt();
+                signaturesSigAlgorithms.add(sigAlgorithm);
+                if (!isSupportedSignatureAlgorithm(sigAlgorithm)) {
+                    continue;
+                }
+                if ((bestSigAlgorithm == -1)
+                        || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
+                    bestSigAlgorithm = sigAlgorithm;
+                    bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature);
+                }
+            } catch (IOException | BufferUnderflowException e) {
+                throw new SecurityException(
+                        "Failed to parse signature record #" + signatureCount,
+                        e);
+            }
+        }
+        if (bestSigAlgorithm == -1) {
+            if (signatureCount == 0) {
+                throw new SecurityException("No signatures found");
+            } else {
+                throw new SecurityException("No supported signatures found");
+            }
+        }
+
+        String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm);
+        Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
+                getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm);
+        String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
+        AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
+        boolean sigVerified;
+        try {
+            PublicKey publicKey =
+                    KeyFactory.getInstance(keyAlgorithm)
+                            .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
+            Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
+            sig.initVerify(publicKey);
+            if (jcaSignatureAlgorithmParams != null) {
+                sig.setParameter(jcaSignatureAlgorithmParams);
+            }
+            sig.update(signedData);
+            sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
+        } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
+                | InvalidAlgorithmParameterException | SignatureException e) {
+            throw new SecurityException(
+                    "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
+        }
+        if (!sigVerified) {
+            throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
+        }
+
+        // Signature over signedData has verified.
+
+        byte[] contentDigest = null;
+        signedData.clear();
+        ByteBuffer digests = getLengthPrefixedSlice(signedData);
+        List<Integer> digestsSigAlgorithms = new ArrayList<>();
+        int digestCount = 0;
+        while (digests.hasRemaining()) {
+            digestCount++;
+            try {
+                ByteBuffer digest = getLengthPrefixedSlice(digests);
+                if (digest.remaining() < 8) {
+                    throw new IOException("Record too short");
+                }
+                int sigAlgorithm = digest.getInt();
+                digestsSigAlgorithms.add(sigAlgorithm);
+                if (sigAlgorithm == bestSigAlgorithm) {
+                    contentDigest = readLengthPrefixedByteArray(digest);
+                }
+            } catch (IOException | BufferUnderflowException e) {
+                throw new IOException("Failed to parse digest record #" + digestCount, e);
+            }
+        }
+
+        if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) {
+            throw new SecurityException(
+                    "Signature algorithms don't match between digests and signatures records");
+        }
+        int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm);
+        byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest);
+        if ((previousSignerDigest != null)
+                && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) {
+            throw new SecurityException(
+                    getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+                    + " contents digest does not match the digest specified by a preceding signer");
+        }
+
+        ByteBuffer certificates = getLengthPrefixedSlice(signedData);
+        List<X509Certificate> certs = new ArrayList<>();
+        int certificateCount = 0;
+        while (certificates.hasRemaining()) {
+            certificateCount++;
+            byte[] encodedCert = readLengthPrefixedByteArray(certificates);
+            X509Certificate certificate;
+            try {
+                certificate = (X509Certificate)
+                        certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
+            } catch (CertificateException e) {
+                throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
+            }
+            certificate = new VerbatimX509Certificate(certificate, encodedCert);
+            certs.add(certificate);
+        }
+
+        if (certs.isEmpty()) {
+            throw new SecurityException("No certificates listed");
+        }
+        X509Certificate mainCertificate = certs.get(0);
+        byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
+        if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
+            throw new SecurityException(
+                    "Public key mismatch between certificate and signature record");
+        }
+
+        return certs.toArray(new X509Certificate[certs.size()]);
+    }
+
+    private static void verifyIntegrity(
+            Map<Integer, byte[]> expectedDigests,
+            ByteBuffer apkContents,
+            int apkSigningBlockOffset,
+            int centralDirOffset,
+            int eocdOffset) throws SecurityException {
+
+        if (expectedDigests.isEmpty()) {
+            throw new SecurityException("No digests provided");
+        }
+
+        ByteBuffer beforeApkSigningBlock = sliceFromTo(apkContents, 0, apkSigningBlockOffset);
+        ByteBuffer centralDir = sliceFromTo(apkContents, centralDirOffset, eocdOffset);
+        // For the purposes of integrity verification, ZIP End of Central Directory's field Start of
+        // Central Directory must be considered to point to the offset of the APK Signing Block.
+        byte[] eocdBytes = new byte[apkContents.capacity() - eocdOffset];
+        apkContents.position(eocdOffset);
+        apkContents.get(eocdBytes);
+        ByteBuffer eocd = ByteBuffer.wrap(eocdBytes);
+        eocd.order(apkContents.order());
+        ZipUtils.setZipEocdCentralDirectoryOffset(eocd, apkSigningBlockOffset);
+
+        int[] digestAlgorithms = new int[expectedDigests.size()];
+        int digestAlgorithmCount = 0;
+        for (int digestAlgorithm : expectedDigests.keySet()) {
+            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
+            digestAlgorithmCount++;
+        }
+        Map<Integer, byte[]> actualDigests;
+        try {
+            actualDigests =
+                    computeContentDigests(
+                            digestAlgorithms,
+                            new ByteBuffer[] {beforeApkSigningBlock, centralDir, eocd});
+        } catch (DigestException e) {
+            throw new SecurityException("Failed to compute digest(s) of contents", e);
+        }
+        for (Map.Entry<Integer, byte[]> entry : expectedDigests.entrySet()) {
+            int digestAlgorithm = entry.getKey();
+            byte[] expectedDigest = entry.getValue();
+            byte[] actualDigest = actualDigests.get(digestAlgorithm);
+            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
+                throw new SecurityException(
+                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+                        + " digest of contents did not verify");
+            }
+        }
+    }
+
+    private static Map<Integer, byte[]> computeContentDigests(
+            int[] digestAlgorithms,
+            ByteBuffer[] contents) throws DigestException {
+        // For each digest algorithm the result is computed as follows:
+        // 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
+        //    The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
+        //    No chunks are produced for empty (zero length) segments.
+        // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's
+        //    length in bytes (uint32 little-endian) and the chunk's contents.
+        // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of
+        //    chunks (uint32 little-endian) and the concatenation of digests of chunks of all
+        //    segments in-order.
+
+        int totalChunkCount = 0;
+        for (ByteBuffer input : contents) {
+            totalChunkCount += getChunkCount(input.remaining());
+        }
+
+        Map<Integer, byte[]> digestsOfChunks = new HashMap<>(totalChunkCount);
+        for (int digestAlgorithm : digestAlgorithms) {
+            int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
+            byte[] concatenationOfChunkCountAndChunkDigests =
+                    new byte[5 + totalChunkCount * digestOutputSizeBytes];
+            concatenationOfChunkCountAndChunkDigests[0] = 0x5a;
+            setUnsignedInt32LittleEndian(
+                    totalChunkCount,
+                    concatenationOfChunkCountAndChunkDigests,
+                    1);
+            digestsOfChunks.put(digestAlgorithm, concatenationOfChunkCountAndChunkDigests);
+        }
+
+        byte[] chunkContentPrefix = new byte[5];
+        chunkContentPrefix[0] = (byte) 0xa5;
+        int chunkIndex = 0;
+        for (ByteBuffer input : contents) {
+            while (input.hasRemaining()) {
+                int chunkSize = Math.min(input.remaining(), CHUNK_SIZE_BYTES);
+                ByteBuffer chunk = getByteBuffer(input, chunkSize);
+                for (int digestAlgorithm : digestAlgorithms) {
+                    String jcaAlgorithmName =
+                            getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
+                    MessageDigest md;
+                    try {
+                        md = MessageDigest.getInstance(jcaAlgorithmName);
+                    } catch (NoSuchAlgorithmException e) {
+                        throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
+                    }
+                    chunk.clear();
+                    setUnsignedInt32LittleEndian(chunk.remaining(), chunkContentPrefix, 1);
+                    md.update(chunkContentPrefix);
+                    md.update(chunk);
+                    byte[] concatenationOfChunkCountAndChunkDigests =
+                            digestsOfChunks.get(digestAlgorithm);
+                    int expectedDigestSizeBytes =
+                            getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
+                    int actualDigestSizeBytes = md.digest(concatenationOfChunkCountAndChunkDigests,
+                            5 + chunkIndex * expectedDigestSizeBytes, expectedDigestSizeBytes);
+                    if (actualDigestSizeBytes != expectedDigestSizeBytes) {
+                        throw new RuntimeException(
+                                "Unexpected output size of " + md.getAlgorithm() + " digest: "
+                                        + actualDigestSizeBytes);
+                    }
+                }
+                chunkIndex++;
+            }
+        }
+
+        Map<Integer, byte[]> result = new HashMap<>(digestAlgorithms.length);
+        for (Map.Entry<Integer, byte[]> entry : digestsOfChunks.entrySet()) {
+            int digestAlgorithm = entry.getKey();
+            byte[] input = entry.getValue();
+            String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
+            MessageDigest md;
+            try {
+                md = MessageDigest.getInstance(jcaAlgorithmName);
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
+            }
+            byte[] output = md.digest(input);
+            result.put(digestAlgorithm, output);
+        }
+        return result;
+    }
+
+    private static final int getChunkCount(int inputSizeBytes) {
+        return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES;
+    }
+
+    private static final int CHUNK_SIZE_BYTES = 1024 * 1024;
+
+    private static final int SIGNATURE_RSA_PSS_WITH_SHA256 = 0x0101;
+    private static final int SIGNATURE_RSA_PSS_WITH_SHA512 = 0x0102;
+    private static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0103;
+    private static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 = 0x0104;
+    private static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
+    private static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
+    private static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
+    private static final int SIGNATURE_DSA_WITH_SHA512 = 0x0302;
+
+    private static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
+    private static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
+
+    private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
+        switch (sigAlgorithm) {
+            case SIGNATURE_RSA_PSS_WITH_SHA256:
+            case SIGNATURE_RSA_PSS_WITH_SHA512:
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+            case SIGNATURE_ECDSA_WITH_SHA256:
+            case SIGNATURE_ECDSA_WITH_SHA512:
+            case SIGNATURE_DSA_WITH_SHA256:
+            case SIGNATURE_DSA_WITH_SHA512:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
+        int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
+        int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
+        return compareContentDigestAlgorithm(digestAlgorithm1, digestAlgorithm2);
+    }
+
+    private static int compareContentDigestAlgorithm(int digestAlgorithm1, int digestAlgorithm2) {
+        switch (digestAlgorithm1) {
+            case CONTENT_DIGEST_CHUNKED_SHA256:
+                switch (digestAlgorithm2) {
+                    case CONTENT_DIGEST_CHUNKED_SHA256:
+                        return 0;
+                    case CONTENT_DIGEST_CHUNKED_SHA512:
+                        return -1;
+                    default:
+                        throw new IllegalArgumentException(
+                                "Unknown digestAlgorithm2: " + digestAlgorithm2);
+                }
+            case CONTENT_DIGEST_CHUNKED_SHA512:
+                switch (digestAlgorithm2) {
+                    case CONTENT_DIGEST_CHUNKED_SHA256:
+                        return 1;
+                    case CONTENT_DIGEST_CHUNKED_SHA512:
+                        return 0;
+                    default:
+                        throw new IllegalArgumentException(
+                                "Unknown digestAlgorithm2: " + digestAlgorithm2);
+                }
+            default:
+                throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1);
+        }
+    }
+
+    private static int getSignatureAlgorithmContentDigestAlgorithm(int sigAlgorithm) {
+        switch (sigAlgorithm) {
+            case SIGNATURE_RSA_PSS_WITH_SHA256:
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+            case SIGNATURE_ECDSA_WITH_SHA256:
+            case SIGNATURE_DSA_WITH_SHA256:
+                return CONTENT_DIGEST_CHUNKED_SHA256;
+            case SIGNATURE_RSA_PSS_WITH_SHA512:
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+            case SIGNATURE_ECDSA_WITH_SHA512:
+            case SIGNATURE_DSA_WITH_SHA512:
+                return CONTENT_DIGEST_CHUNKED_SHA512;
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown signature algorithm: 0x"
+                                + Long.toHexString(sigAlgorithm & 0xffffffff));
+        }
+    }
+
+    private static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) {
+        switch (digestAlgorithm) {
+            case CONTENT_DIGEST_CHUNKED_SHA256:
+                return "SHA-256";
+            case CONTENT_DIGEST_CHUNKED_SHA512:
+                return "SHA-512";
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown content digest algorthm: " + digestAlgorithm);
+        }
+    }
+
+    private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) {
+        switch (digestAlgorithm) {
+            case CONTENT_DIGEST_CHUNKED_SHA256:
+                return 256 / 8;
+            case CONTENT_DIGEST_CHUNKED_SHA512:
+                return 512 / 8;
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown content digest algorthm: " + digestAlgorithm);
+        }
+    }
+
+    private static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) {
+        switch (sigAlgorithm) {
+            case SIGNATURE_RSA_PSS_WITH_SHA256:
+            case SIGNATURE_RSA_PSS_WITH_SHA512:
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+                return "RSA";
+            case SIGNATURE_ECDSA_WITH_SHA256:
+            case SIGNATURE_ECDSA_WITH_SHA512:
+                return "EC";
+            case SIGNATURE_DSA_WITH_SHA256:
+            case SIGNATURE_DSA_WITH_SHA512:
+                return "DSA";
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown signature algorithm: 0x"
+                                + Long.toHexString(sigAlgorithm & 0xffffffff));
+        }
+    }
+
+    private static Pair<String, ? extends AlgorithmParameterSpec>
+            getSignatureAlgorithmJcaSignatureAlgorithm(int sigAlgorithm) {
+        switch (sigAlgorithm) {
+            case SIGNATURE_RSA_PSS_WITH_SHA256:
+                return Pair.create(
+                        "SHA256withRSA/PSS",
+                        new PSSParameterSpec(
+                                "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1));
+            case SIGNATURE_RSA_PSS_WITH_SHA512:
+                return Pair.create(
+                        "SHA512withRSA/PSS",
+                        new PSSParameterSpec(
+                                "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1));
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+                return Pair.create("SHA256withRSA", null);
+            case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+                return Pair.create("SHA512withRSA", null);
+            case SIGNATURE_ECDSA_WITH_SHA256:
+                return Pair.create("SHA256withECDSA", null);
+            case SIGNATURE_ECDSA_WITH_SHA512:
+                return Pair.create("SHA512withECDSA", null);
+            case SIGNATURE_DSA_WITH_SHA256:
+                return Pair.create("SHA256withDSA", null);
+            case SIGNATURE_DSA_WITH_SHA512:
+                return Pair.create("SHA512withDSA", null);
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown signature algorithm: 0x"
+                                + Long.toHexString(sigAlgorithm & 0xffffffff));
+        }
+    }
+
+    /**
+     * Returns new byte buffer whose content is a shared subsequence of this buffer's content
+     * between the specified start (inclusive) and end (exclusive) positions. As opposed to
+     * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
+     * buffer's byte order.
+     */
+    private static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) {
+        if (start < 0) {
+            throw new IllegalArgumentException("start: " + start);
+        }
+        if (end < start) {
+            throw new IllegalArgumentException("end < start: " + end + " < " + start);
+        }
+        int capacity = source.capacity();
+        if (end > source.capacity()) {
+            throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity);
+        }
+        int originalLimit = source.limit();
+        int originalPosition = source.position();
+        try {
+            source.position(0);
+            source.limit(end);
+            source.position(start);
+            ByteBuffer result = source.slice();
+            result.order(source.order());
+            return result;
+        } finally {
+            source.position(0);
+            source.limit(originalLimit);
+            source.position(originalPosition);
+        }
+    }
+
+    /**
+     * Relative <em>get</em> method for reading {@code size} number of bytes from the current
+     * position of this buffer.
+     *
+     * <p>This method reads the next {@code size} bytes at this buffer's current position,
+     * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to
+     * {@code size}, byte order set to this buffer's byte order; and then increments the position by
+     * {@code size}.
+     */
+    private static ByteBuffer getByteBuffer(ByteBuffer source, int size)
+            throws BufferUnderflowException {
+        if (size < 0) {
+            throw new IllegalArgumentException("size: " + size);
+        }
+        int originalLimit = source.limit();
+        int position = source.position();
+        int limit = position + size;
+        if ((limit < position) || (limit > originalLimit)) {
+            throw new BufferUnderflowException();
+        }
+        source.limit(limit);
+        try {
+            ByteBuffer result = source.slice();
+            result.order(source.order());
+            source.position(limit);
+            return result;
+        } finally {
+            source.limit(originalLimit);
+        }
+    }
+
+    private static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws IOException {
+        if (source.remaining() < 4) {
+            throw new IOException(
+                    "Remaining buffer too short to contain length of length-prefixed field."
+                            + " Remaining: " + source.remaining());
+        }
+        int len = source.getInt();
+        if (len < 0) {
+            throw new IllegalArgumentException("Negative length");
+        } else if (len > source.remaining()) {
+            throw new IOException("Length-prefixed field longer than remaining buffer."
+                    + " Field length: " + len + ", remaining: " + source.remaining());
+        }
+        return getByteBuffer(source, len);
+    }
+
+    private static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws IOException {
+        int len = buf.getInt();
+        if (len < 0) {
+            throw new IOException("Negative length");
+        } else if (len > buf.remaining()) {
+            throw new IOException("Underflow while reading length-prefixed value. Length: " + len
+                    + ", available: " + buf.remaining());
+        }
+        byte[] result = new byte[len];
+        buf.get(result);
+        return result;
+    }
+
+    private static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) {
+        result[offset] = (byte) (value & 0xff);
+        result[offset + 1] = (byte) ((value >>> 8) & 0xff);
+        result[offset + 2] = (byte) ((value >>> 16) & 0xff);
+        result[offset + 3] = (byte) ((value >>> 24) & 0xff);
+    }
+
+    private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L;
+    private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L;
+    private static final int APK_SIG_BLOCK_MIN_SIZE = 32;
+
+    private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
+
+    private static int findApkSigningBlock(ByteBuffer apkContents, int centralDirOffset)
+            throws SignatureNotFoundException {
+        checkByteOrderLittleEndian(apkContents);
+
+        // FORMAT:
+        // OFFSET       DATA TYPE  DESCRIPTION
+        // * @+0  bytes uint64:    size in bytes (excluding this field)
+        // * @+8  bytes payload
+        // * @-24 bytes uint64:    size in bytes (same as the one above)
+        // * @-16 bytes uint128:   magic
+
+        if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) {
+            throw new SignatureNotFoundException(
+                    "APK too small for APK Signing Block. ZIP Central Directory offset: "
+                            + centralDirOffset);
+        }
+        // Check magic field present
+        if ((apkContents.getLong(centralDirOffset - 16) != APK_SIG_BLOCK_MAGIC_LO)
+                || (apkContents.getLong(centralDirOffset - 8) != APK_SIG_BLOCK_MAGIC_HI)) {
+            throw new SignatureNotFoundException(
+                    "No APK Signing Block before ZIP Central Directory");
+        }
+        // Read and compare size fields
+        long apkSigBlockSizeLong = apkContents.getLong(centralDirOffset - 24);
+        if ((apkSigBlockSizeLong < 24) || (apkSigBlockSizeLong > Integer.MAX_VALUE - 8)) {
+            throw new SignatureNotFoundException(
+                    "APK Signing Block size out of range: " + apkSigBlockSizeLong);
+        }
+        int apkSigBlockSizeFromFooter = (int) apkSigBlockSizeLong;
+        int totalSize = apkSigBlockSizeFromFooter + 8;
+        int apkSigBlockOffset = centralDirOffset - totalSize;
+        if (apkSigBlockOffset < 0) {
+            throw new SignatureNotFoundException(
+                    "APK Signing Block offset out of range: " + apkSigBlockOffset);
+        }
+        long apkSigBlockSizeFromHeader = apkContents.getLong(apkSigBlockOffset);
+        if (apkSigBlockSizeFromHeader != apkSigBlockSizeFromFooter) {
+            throw new SignatureNotFoundException(
+                    "APK Signing Block sizes in header and footer do not match: "
+                            + apkSigBlockSizeFromHeader + " vs " + apkSigBlockSizeFromFooter);
+        }
+        return apkSigBlockOffset;
+    }
+
+    private static ByteBuffer findApkSignatureSchemeV2Block(ByteBuffer apkSigningBlock)
+            throws SignatureNotFoundException {
+        checkByteOrderLittleEndian(apkSigningBlock);
+        // FORMAT:
+        // OFFSET       DATA TYPE  DESCRIPTION
+        // * @+0  bytes uint64:    size in bytes (excluding this field)
+        // * @+8  bytes pairs
+        // * @-24 bytes uint64:    size in bytes (same as the one above)
+        // * @-16 bytes uint128:   magic
+        ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
+
+        int entryCount = 0;
+        while (pairs.hasRemaining()) {
+            entryCount++;
+            if (pairs.remaining() < 8) {
+                throw new SignatureNotFoundException(
+                        "Insufficient data to read size of APK Signing Block entry #" + entryCount);
+            }
+            long lenLong = pairs.getLong();
+            if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
+                throw new SignatureNotFoundException(
+                        "APK Signing Block entry #" + entryCount
+                                + " size out of range: " + lenLong);
+            }
+            int len = (int) lenLong;
+            int nextEntryPos = pairs.position() + len;
+            if (len > pairs.remaining()) {
+                throw new SignatureNotFoundException(
+                        "APK Signing Block entry #" + entryCount + " size out of range: " + len
+                                + ", available: " + pairs.remaining());
+            }
+            int id = pairs.getInt();
+            if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
+                return getByteBuffer(pairs, len - 4);
+            }
+            pairs.position(nextEntryPos);
+        }
+
+        throw new SignatureNotFoundException(
+                "No APK Signature Scheme v2 block in APK Signing Block");
+    }
+
+    private static void checkByteOrderLittleEndian(ByteBuffer buffer) {
+        if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
+            throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
+        }
+    }
+
+    public static class SignatureNotFoundException extends Exception {
+        public SignatureNotFoundException(String message) {
+            super(message);
+        }
+
+        public SignatureNotFoundException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
+    /**
+     * For legacy reasons we need to return exactly the original encoded certificate bytes, instead
+     * of letting the underlying implementation have a shot at re-encoding the data.
+     */
+    private static class VerbatimX509Certificate extends WrappedX509Certificate {
+        private byte[] encodedVerbatim;
+
+        public VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
+            super(wrapped);
+            this.encodedVerbatim = encodedVerbatim;
+        }
+
+        @Override
+        public byte[] getEncoded() throws CertificateEncodingException {
+            return encodedVerbatim;
+        }
+    }
+
+    private static class WrappedX509Certificate extends X509Certificate {
+        private final X509Certificate wrapped;
+
+        public WrappedX509Certificate(X509Certificate wrapped) {
+            this.wrapped = wrapped;
+        }
+
+        @Override
+        public Set<String> getCriticalExtensionOIDs() {
+            return wrapped.getCriticalExtensionOIDs();
+        }
+
+        @Override
+        public byte[] getExtensionValue(String oid) {
+            return wrapped.getExtensionValue(oid);
+        }
+
+        @Override
+        public Set<String> getNonCriticalExtensionOIDs() {
+            return wrapped.getNonCriticalExtensionOIDs();
+        }
+
+        @Override
+        public boolean hasUnsupportedCriticalExtension() {
+            return wrapped.hasUnsupportedCriticalExtension();
+        }
+
+        @Override
+        public void checkValidity()
+                throws CertificateExpiredException, CertificateNotYetValidException {
+            wrapped.checkValidity();
+        }
+
+        @Override
+        public void checkValidity(Date date)
+                throws CertificateExpiredException, CertificateNotYetValidException {
+            wrapped.checkValidity(date);
+        }
+
+        @Override
+        public int getVersion() {
+            return wrapped.getVersion();
+        }
+
+        @Override
+        public BigInteger getSerialNumber() {
+            return wrapped.getSerialNumber();
+        }
+
+        @Override
+        public Principal getIssuerDN() {
+            return wrapped.getIssuerDN();
+        }
+
+        @Override
+        public Principal getSubjectDN() {
+            return wrapped.getSubjectDN();
+        }
+
+        @Override
+        public Date getNotBefore() {
+            return wrapped.getNotBefore();
+        }
+
+        @Override
+        public Date getNotAfter() {
+            return wrapped.getNotAfter();
+        }
+
+        @Override
+        public byte[] getTBSCertificate() throws CertificateEncodingException {
+            return wrapped.getTBSCertificate();
+        }
+
+        @Override
+        public byte[] getSignature() {
+            return wrapped.getSignature();
+        }
+
+        @Override
+        public String getSigAlgName() {
+            return wrapped.getSigAlgName();
+        }
+
+        @Override
+        public String getSigAlgOID() {
+            return wrapped.getSigAlgOID();
+        }
+
+        @Override
+        public byte[] getSigAlgParams() {
+            return wrapped.getSigAlgParams();
+        }
+
+        @Override
+        public boolean[] getIssuerUniqueID() {
+            return wrapped.getIssuerUniqueID();
+        }
+
+        @Override
+        public boolean[] getSubjectUniqueID() {
+            return wrapped.getSubjectUniqueID();
+        }
+
+        @Override
+        public boolean[] getKeyUsage() {
+            return wrapped.getKeyUsage();
+        }
+
+        @Override
+        public int getBasicConstraints() {
+            return wrapped.getBasicConstraints();
+        }
+
+        @Override
+        public byte[] getEncoded() throws CertificateEncodingException {
+            return wrapped.getEncoded();
+        }
+
+        @Override
+        public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+                InvalidKeyException, NoSuchProviderException, SignatureException {
+            wrapped.verify(key);
+        }
+
+        @Override
+        public void verify(PublicKey key, String sigProvider)
+                throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+                NoSuchProviderException, SignatureException {
+            wrapped.verify(key, sigProvider);
+        }
+
+        @Override
+        public String toString() {
+            return wrapped.toString();
+        }
+
+        @Override
+        public PublicKey getPublicKey() {
+            return wrapped.getPublicKey();
+        }
+    }
+}
diff --git a/core/java/android/util/apk/ZipUtils.java b/core/java/android/util/apk/ZipUtils.java
new file mode 100644
index 0000000..a383d5c
--- /dev/null
+++ b/core/java/android/util/apk/ZipUtils.java
@@ -0,0 +1,163 @@
+/*
+ * 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.util.apk;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Assorted ZIP format helpers.
+ *
+ * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances except that the byte
+ * order of these buffers is little-endian.
+ */
+abstract class ZipUtils {
+    private ZipUtils() {}
+
+    private static final int ZIP_EOCD_REC_MIN_SIZE = 22;
+    private static final int ZIP_EOCD_REC_SIG = 0x06054b50;
+    private static final int ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET = 12;
+    private static final int ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET = 16;
+    private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20;
+
+    private static final int ZIP64_EOCD_LOCATOR_SIZE = 20;
+    private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50;
+
+    private static final int UINT32_MAX_VALUE = 0xffff;
+
+    /**
+     * Returns the position at which ZIP End of Central Directory record starts in the provided
+     * buffer or {@code -1} if the record is not present.
+     *
+     * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
+     */
+    public static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {
+        assertByteOrderLittleEndian(zipContents);
+
+        // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
+        // The record can be identified by its 4-byte signature/magic which is located at the very
+        // beginning of the record. A complication is that the record is variable-length because of
+        // the comment field.
+        // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
+        // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
+        // the candidate record's comment length is such that the remainder of the record takes up
+        // exactly the remaining bytes in the buffer. The search is bounded because the maximum
+        // size of the comment field is 65535 bytes because the field is an unsigned 32-bit number.
+
+        int archiveSize = zipContents.capacity();
+        if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) {
+            System.out.println("File size smaller than EOCD min size");
+            return -1;
+        }
+        int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT32_MAX_VALUE);
+        int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE;
+        for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength;
+                expectedCommentLength++) {
+            int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength;
+            if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) {
+                int actualCommentLength =
+                        getUnsignedInt16(
+                                zipContents, eocdStartPos + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET);
+                if (actualCommentLength == expectedCommentLength) {
+                    return eocdStartPos;
+                }
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns {@code true} if the provided buffer contains a ZIP64 End of Central Directory
+     * Locator.
+     *
+     * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
+     */
+    public static final boolean isZip64EndOfCentralDirectoryLocatorPresent(
+            ByteBuffer zipContents, int zipEndOfCentralDirectoryPosition) {
+        assertByteOrderLittleEndian(zipContents);
+
+        // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central
+        // Directory Record.
+
+        int locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE;
+        if (locatorPosition < 0) {
+            return false;
+        }
+
+        return zipContents.getInt(locatorPosition) == ZIP64_EOCD_LOCATOR_SIG;
+    }
+
+    /**
+     * Returns the offset of the start of the ZIP Central Directory in the archive.
+     *
+     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
+     */
+    public static long getZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory) {
+        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
+        return getUnsignedInt32(
+                zipEndOfCentralDirectory,
+                zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);
+    }
+
+    /**
+     * Sets the offset of the start of the ZIP Central Directory in the archive.
+     *
+     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
+     */
+    public static void setZipEocdCentralDirectoryOffset(
+            ByteBuffer zipEndOfCentralDirectory, long offset) {
+        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
+        setUnsignedInt32(
+                zipEndOfCentralDirectory,
+                zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET,
+                offset);
+    }
+
+    /**
+     * Returns the size (in bytes) of the ZIP Central Directory.
+     *
+     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
+     */
+    public static long getZipEocdCentralDirectorySizeBytes(ByteBuffer zipEndOfCentralDirectory) {
+        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
+        return getUnsignedInt32(
+                zipEndOfCentralDirectory,
+                zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET);
+    }
+
+    private static void assertByteOrderLittleEndian(ByteBuffer buffer) {
+        if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
+            throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
+        }
+    }
+
+    private static int getUnsignedInt16(ByteBuffer buffer, int offset) {
+        return buffer.getShort(offset) & 0xffff;
+    }
+
+    private static long getUnsignedInt32(ByteBuffer buffer, int offset) {
+        return buffer.getInt(offset) & 0xffffffffL;
+    }
+
+    private static void setUnsignedInt32(ByteBuffer buffer, int offset, long value) {
+        if ((value < 0) || (value > 0xffffffffL)) {
+            throw new IllegalArgumentException("uint32 value of out range: " + value);
+        }
+        buffer.putInt(buffer.position() + offset, (int) value);
+    }
+}
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index fd57806..302a08d 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -18,7 +18,6 @@
 package android.util.jar;
 
 import dalvik.system.CloseGuard;
-import java.io.ByteArrayInputStream;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -30,9 +29,7 @@
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
 import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 import java.util.jar.JarFile;
-import java.util.jar.Manifest;
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
@@ -59,7 +56,13 @@
     private final CloseGuard guard = CloseGuard.get();
     private boolean closed;
 
-    public StrictJarFile(String fileName) throws IOException, SecurityException {
+    public StrictJarFile(String fileName)
+            throws IOException, SecurityException {
+        this(fileName, true);
+    }
+
+    public StrictJarFile(String fileName, boolean verify)
+            throws IOException, SecurityException {
         this.nativeHandle = nativeOpenJarFile(fileName);
         this.raf = new RandomAccessFile(fileName, "r");
 
@@ -67,17 +70,23 @@
             // Read the MANIFEST and signature files up front and try to
             // parse them. We never want to accept a JAR File with broken signatures
             // or manifests, so it's best to throw as early as possible.
-            HashMap<String, byte[]> metaEntries = getMetaEntries();
-            this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
-            this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
-            Set<String> files = manifest.getEntries().keySet();
-            for (String file : files) {
-                if (findEntry(file) == null) {
-                    throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+            if (verify) {
+                HashMap<String, byte[]> metaEntries = getMetaEntries();
+                this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
+                this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
+                Set<String> files = manifest.getEntries().keySet();
+                for (String file : files) {
+                    if (findEntry(file) == null) {
+                        throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+                    }
                 }
-            }
 
-            isSigned = verifier.readCertificates() && verifier.isSignedJar();
+                isSigned = verifier.readCertificates() && verifier.isSignedJar();
+            } else {
+                isSigned = false;
+                this.manifest = null;
+                this.verifier = null;
+            }
         } catch (IOException | SecurityException e) {
             nativeClose(this.nativeHandle);
             IoUtils.closeQuietly(this.raf);
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index ca2aec1..0546a5f 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -32,8 +32,12 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
+import android.util.ArraySet;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
 import libcore.io.Base64;
 import sun.security.jca.Providers;
 import sun.security.pkcs.PKCS7;
@@ -353,6 +357,43 @@
             return;
         }
 
+        // Check whether APK Signature Scheme v2 signature was stripped.
+        String apkSignatureSchemeIdList =
+                attributes.getValue(
+                        ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
+        if (apkSignatureSchemeIdList != null) {
+            // This field contains a comma-separated list of APK signature scheme IDs which were
+            // used to sign this APK. If an ID is known to us, it means signatures of that scheme
+            // were stripped from the APK because otherwise we wouldn't have fallen back to
+            // verifying the APK using the JAR signature scheme.
+            boolean v2SignatureGenerated = false;
+            StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
+            while (tokenizer.hasMoreTokens()) {
+                String idText = tokenizer.nextToken().trim();
+                if (idText.isEmpty()) {
+                    continue;
+                }
+                int id;
+                try {
+                    id = Integer.parseInt(idText);
+                } catch (Exception ignored) {
+                    continue;
+                }
+                if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
+                    // This APK was supposed to be signed with APK Signature Scheme v2 but no such
+                    // signature was found.
+                    v2SignatureGenerated = true;
+                    break;
+                }
+            }
+
+            if (v2SignatureGenerated) {
+                throw new SecurityException(signatureFile + " indicates " + jarName + " is signed"
+                        + " using APK Signature Scheme v2, but no such signature was found."
+                        + " Signature stripped?");
+            }
+        }
+
         // Do we actually have any signatures to look at?
         if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) {
             return;
diff --git a/core/java/android/view/IDockDividerVisibilityListener.aidl b/core/java/android/view/IDockedStackListener.aidl
similarity index 70%
rename from core/java/android/view/IDockDividerVisibilityListener.aidl
rename to core/java/android/view/IDockedStackListener.aidl
index a7d5cda..77fa7e2 100644
--- a/core/java/android/view/IDockDividerVisibilityListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -22,6 +22,15 @@
   *
   * @hide
   */
-oneway interface IDockDividerVisibilityListener {
-    void onDockDividerVisibilityChanged(boolean visible);
+oneway interface IDockedStackListener {
+
+    /**
+     * Will fire when an app is shown in side by side mode and a divider should be shown.
+     */
+    void onDividerVisibilityChanged(boolean visible);
+
+    /**
+     * Called when the docked stack gets created or removed.
+     */
+    void onDockedStackExistsChanged(boolean exists);
 }
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 9e478c1..9231394 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -25,6 +25,8 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import com.android.internal.os.IResultReceiver;
+
 /**
  * API back to a client window that the Window Manager uses to inform it of
  * interesting things happening.
@@ -83,4 +85,9 @@
      * Called for non-application windows when the enter animation has completed.
      */
     void dispatchWindowShown();
+
+    /**
+     * Called when Keyboard Shortcuts are requested for the window.
+     */
+    void requestAppKeyboardShortcuts(IResultReceiver receiver);
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7a379d50..b045c17 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -17,6 +17,7 @@
 package android.view;
 
 import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 
@@ -29,7 +30,7 @@
 import android.os.IRemoteCallback;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IDockDividerVisibilityListener;
+import android.view.IDockedStackListener;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
@@ -101,11 +102,14 @@
      *                   the task doesn't exist yet.
      * @param configuration Configuration that is being used with this task.
      * @param cropWindowsToStack True if the app windows should be cropped to the stack bounds.
+     * @param alwaysFocusable True if the app windows are always focusable regardless of the stack
+     *                        they are in.
      */
     void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
-            in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack);
+            in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack,
+            boolean alwaysFocusable);
     /**
      *
      * @param token The token we are adding to the input task Id.
@@ -354,7 +358,24 @@
     void setDockedStackResizing(boolean resizing);
 
     /**
-     * Registers a listener that will be called when the dock divider changes its visibility.
+     * Registers a listener that will be called when the dock divider changes its visibility or when
+     * the docked stack gets added/removed.
      */
-    void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener);
+    void registerDockedStackListener(IDockedStackListener listener);
+
+    /**
+     * Updates the dim layer used while resizing.
+     *
+     * @param visible Whether the dim layer should be visible.
+     * @param targetStackId The id of the task stack the dim layer should be placed on.
+     * @param alpha The translucency of the dim layer, between 0 and 1.
+     */
+    void setResizeDimLayer(boolean visible, int targetStackId, float alpha);
+
+    /**
+     * Requests Keyboard Shortcuts from the displayed window.
+     *
+     * @param receiver The receiver to deliver the results to.
+     */
+    void requestAppKeyboardShortcuts(IResultReceiver receiver);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index b3cd8c11..bea36c0 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -81,6 +81,8 @@
      * so complex relayout of the window should not happen based on them.
      * @param outOutsets Rect in which is placed the dead area of the screen that we would like to
      * treat as real display. Example of such area is a chin in some models of wearable devices.
+     * @param outBackdropFrame Rect which is used draw the resizing background during a resize
+     * operation.
      * @param outConfiguration New configuration of window, if it is now
      * becoming visible and the global configuration has changed since it
      * was last displayed.
@@ -93,7 +95,8 @@
             int requestedWidth, int requestedHeight, int viewVisibility,
             int flags, out Rect outFrame, out Rect outOverscanInsets,
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
-            out Rect outOutsets, out Configuration outConfig, out Surface outSurface);
+            out Rect outOutsets, out Rect outBackdropFrame, out Configuration outConfig,
+            out Surface outSurface);
 
     /**
      *  Position a window relative to it's parent (attached) window without triggering
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index f037958..51e1f4b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1856,6 +1856,9 @@
             case KeyEvent.KEYCODE_MENU:
             case KeyEvent.KEYCODE_WAKEUP:
             case KeyEvent.KEYCODE_PAIRING:
+            case KeyEvent.KEYCODE_STEM_1:
+            case KeyEvent.KEYCODE_STEM_2:
+            case KeyEvent.KEYCODE_STEM_3:
                 return true;
         }
         return false;
diff --git a/core/java/android/view/KeyboardShortcutGroup.java b/core/java/android/view/KeyboardShortcutGroup.java
new file mode 100644
index 0000000..013255b
--- /dev/null
+++ b/core/java/android/view/KeyboardShortcutGroup.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+/**
+ * A group of {@link KeyboardShortcutInfo}.
+ */
+public final class KeyboardShortcutGroup implements Parcelable {
+    private final CharSequence mLabel;
+    private final List<KeyboardShortcutInfo> mItems;
+
+    /**
+     * @param label The title to be used for this group, or null if there is none.
+     * @param items The set of items to be included.
+     */
+    public KeyboardShortcutGroup(@Nullable CharSequence label,
+            @NonNull List<KeyboardShortcutInfo> items) {
+        mLabel = label;
+        mItems = new ArrayList<>(checkNotNull(items));
+    }
+
+    /**
+     * @param label The title to be used for this group, or null if there is none.
+     */
+    public KeyboardShortcutGroup(@Nullable CharSequence label) {
+        this(label, Collections.<KeyboardShortcutInfo>emptyList());
+    }
+
+    private KeyboardShortcutGroup(Parcel source) {
+        mItems = new ArrayList<>();
+        mLabel = source.readCharSequence();
+        source.readTypedList(mItems, KeyboardShortcutInfo.CREATOR);
+    }
+
+    /**
+     * Returns the label to be used to describe this group.
+     */
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Returns the list of items included in this group.
+     */
+    public List<KeyboardShortcutInfo> getItems() {
+        return mItems;
+    }
+
+    /**
+     * Adds an item to the existing list.
+     *
+     * @param item The item to be added.
+     */
+    public void addItem(KeyboardShortcutInfo item) {
+        mItems.add(item);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeCharSequence(mLabel);
+        dest.writeTypedList(mItems);
+    }
+
+    public static final Creator<KeyboardShortcutGroup> CREATOR =
+            new Creator<KeyboardShortcutGroup>() {
+        public KeyboardShortcutGroup createFromParcel(Parcel source) {
+            return new KeyboardShortcutGroup(source);
+        }
+        public KeyboardShortcutGroup[] newArray(int size) {
+            return new KeyboardShortcutGroup[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
new file mode 100644
index 0000000..2c9006d
--- /dev/null
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.annotation.Nullable;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static java.lang.Character.MIN_VALUE;
+
+/**
+ * Information about a Keyboard Shortcut.
+ */
+public final class KeyboardShortcutInfo implements Parcelable {
+    private final CharSequence mLabel;
+    private final Icon mIcon;
+    private final char mBaseCharacter;
+    private final int mModifiers;
+
+    /**
+     * @param label The label that identifies the action performed by this shortcut.
+     * @param icon An icon that identifies the action performed by this shortcut.
+     * @param baseCharacter The character that triggers the shortcut.
+     * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
+     *     These should be a combination of {@link KeyEvent#META_CTRL_ON},
+     *     {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and
+     *     {@link KeyEvent#META_ALT_ON}.
+     *
+     * @hide
+     */
+    public KeyboardShortcutInfo(
+            @Nullable CharSequence label, @Nullable Icon icon, char baseCharacter, int modifiers) {
+        mLabel = label;
+        mIcon = icon;
+        checkArgument(baseCharacter != MIN_VALUE);
+        mBaseCharacter = baseCharacter;
+        mModifiers = modifiers;
+    }
+
+    /**
+     * Convenience constructor for shortcuts with a label and no icon.
+     *
+     * @param label The label that identifies the action performed by this shortcut.
+     * @param baseCharacter The character that triggers the shortcut.
+     * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
+     *     These should be a combination of {@link KeyEvent#META_CTRL_ON},
+     *     {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and
+     *     {@link KeyEvent#META_ALT_ON}.
+     */
+    public KeyboardShortcutInfo(CharSequence label, char baseCharacter, int modifiers) {
+        mLabel = label;
+        checkArgument(baseCharacter != MIN_VALUE);
+        mBaseCharacter = baseCharacter;
+        mModifiers = modifiers;
+        mIcon = null;
+    }
+
+    private KeyboardShortcutInfo(Parcel source) {
+        mLabel = source.readCharSequence();
+        mIcon = (Icon) source.readParcelable(null);
+        mBaseCharacter = (char) source.readInt();
+        mModifiers = source.readInt();
+    }
+
+    /**
+     * Returns the label to be used to describe this shortcut.
+     */
+    @Nullable
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Returns the icon to be used to describe this shortcut.
+     *
+     * @hide
+     */
+    @Nullable
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Returns the base character that, combined with the modifiers, triggers this shortcut.
+     */
+    public char getBaseCharacter() {
+        return mBaseCharacter;
+    }
+
+    /**
+     * Returns the set of modifiers that, combined with the key, trigger this shortcut.
+     */
+    public int getModifiers() {
+        return mModifiers;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeCharSequence(mLabel);
+        dest.writeParcelable(mIcon, 0);
+        dest.writeInt(mBaseCharacter);
+        dest.writeInt(mModifiers);
+    }
+
+    public static final Creator<KeyboardShortcutInfo> CREATOR =
+            new Creator<KeyboardShortcutInfo>() {
+        public KeyboardShortcutInfo createFromParcel(Parcel source) {
+            return new KeyboardShortcutInfo(source);
+        }
+        public KeyboardShortcutInfo[] newArray(int size) {
+            return new KeyboardShortcutInfo[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 7a544b8..a0f5142 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -973,6 +973,7 @@
      * </p>
      *
      * @see #getAxisValue(int, int)
+     * {@hide}
      */
     public static final int AXIS_SCROLL = 26;
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f4fa980..0981e69 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -107,6 +107,7 @@
     final Rect mContentInsets = new Rect();
     final Rect mStableInsets = new Rect();
     final Rect mOutsets = new Rect();
+    final Rect mBackdropFrame = new Rect();
     final Configuration mConfiguration = new Configuration();
 
     static final int KEEP_SCREEN_ON_MSG = 1;
@@ -529,8 +530,8 @@
                             visible ? VISIBLE : GONE,
                             WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                             mWinFrame, mOverscanInsets, mContentInsets,
-                            mVisibleInsets, mStableInsets, mOutsets, mConfiguration,
-                            mNewSurface);
+                            mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
+                            mConfiguration, mNewSurface);
                     if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                         reportDrawNeeded = true;
                     }
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 6b60be9..1be4810 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -297,7 +297,7 @@
 
     @Override
     public void setForeground(Drawable foreground) {
-        if (foreground != null) {
+        if (foreground != null && !sTextureViewIgnoresDrawableSetters) {
             throw new UnsupportedOperationException(
                     "TextureView doesn't support displaying a foreground drawable");
         }
@@ -305,7 +305,7 @@
 
     @Override
     public void setBackgroundDrawable(Drawable background) {
-        if (background != null) {
+        if (background != null && !sTextureViewIgnoresDrawableSetters) {
             throw new UnsupportedOperationException(
                     "TextureView doesn't support displaying a background drawable");
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 537f887..0b8018b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -79,10 +79,11 @@
 import android.util.SuperNotCalledException;
 import android.util.TypedValue;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
-import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
+import android.view.AccessibilityIterators.TextSegmentIterator;
+import android.view.AccessibilityIterators.WordTextSegmentIterator;
+import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -812,6 +813,12 @@
     private static boolean sLayoutParamsAlwaysChanged = false;
 
     /**
+     * Allow setForeground/setBackground to be called (and ignored) on a textureview,
+     * without throwing
+     */
+    static boolean sTextureViewIgnoresDrawableSetters = false;
+
+    /**
      * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
      * calling setFlags.
      */
@@ -3984,6 +3991,10 @@
             // work. Partial layout breaks this assumption.
             sLayoutParamsAlwaysChanged = targetSdkVersion <= M;
 
+            // Prior to N, TextureView would silently ignore calls to setBackground/setForeground.
+            // On N+, we throw, but that breaks compatibility with apps that use these methods.
+            sTextureViewIgnoresDrawableSetters = targetSdkVersion <= M;
+
             sCompatibilityDone = true;
         }
     }
@@ -17248,8 +17259,10 @@
      */
     @CallSuper
     protected boolean verifyDrawable(Drawable who) {
-        return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who)
-                || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
+        // Avoid verifying the scroll bar drawable so that we don't end up in
+        // an invalidation loop. This effectively prevents the scroll bar
+        // drawable from triggering invalidations and scheduling runnables.
+        return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
     }
 
     /**
@@ -21696,6 +21709,13 @@
     }
 
     /**
+     * @hide
+     */
+    public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> data) {
+        // Do nothing.
+    }
+
+    /**
      * Interface definition for a callback to be invoked when a hardware key event is
      * dispatched to this view. The callback will be invoked before the key event is
      * given to the view. This is only useful for hardware keyboards; a software input
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index cd93dab..f674298 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1419,8 +1419,9 @@
 
         case DragEvent.ACTION_DRAG_ENDED: {
             // Release the bookkeeping now that the drag lifecycle has ended
-            if (mChildrenInterestedInDrag != null) {
-                for (View child : mChildrenInterestedInDrag) {
+            final HashSet<View> childrenInterestedInDrag = mChildrenInterestedInDrag;
+            if (childrenInterestedInDrag != null) {
+                for (View child : childrenInterestedInDrag) {
                     // If a child was interested in the ongoing drag, it's told that it's over
                     if (child.dispatchDragEvent(event)) {
                         retval = true;
@@ -1428,12 +1429,11 @@
                     child.mPrivateFlags2 &= ~View.DRAG_MASK;
                     child.refreshDrawableState();
                 }
-
-                mChildrenInterestedInDrag.clear();
-                if (mCurrentDragStartEvent != null) {
-                    mCurrentDragStartEvent.recycle();
-                    mCurrentDragStartEvent = null;
-                }
+                childrenInterestedInDrag.clear();
+            }
+            if (mCurrentDragStartEvent != null) {
+                mCurrentDragStartEvent.recycle();
+                mCurrentDragStartEvent = null;
             }
 
             if (mIsInterestedInDrag) {
@@ -5359,6 +5359,9 @@
     void offsetRectBetweenParentAndChild(View descendant, Rect rect,
             boolean offsetFromChildToParent, boolean clipToBounds) {
 
+        final RectF rectF = mAttachInfo != null ? mAttachInfo.mTmpTransformRect1 : new RectF();
+        final Matrix inverse = mAttachInfo != null ? mAttachInfo.mTmpMatrix : new Matrix();
+
         // already in the same coord system :)
         if (descendant == this) {
             return;
@@ -5372,8 +5375,16 @@
                 && (theParent != this)) {
 
             if (offsetFromChildToParent) {
-                rect.offset(descendant.mLeft - descendant.mScrollX,
-                        descendant.mTop - descendant.mScrollY);
+                rect.offset(-descendant.mScrollX, -descendant.mScrollY);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    rectF.set(rect);
+                    descendant.getMatrix().mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mLeft, descendant.mTop);
+
                 if (clipToBounds) {
                     View p = (View) theParent;
                     boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
@@ -5391,8 +5402,16 @@
                         rect.setEmpty();
                     }
                 }
-                rect.offset(descendant.mScrollX - descendant.mLeft,
-                        descendant.mScrollY - descendant.mTop);
+                rect.offset(-descendant.mLeft, -descendant.mTop);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    descendant.getMatrix().invert(inverse);
+                    rectF.set(rect);
+                    inverse.mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mScrollX, descendant.mScrollY);
             }
 
             descendant = (View) theParent;
@@ -5403,11 +5422,26 @@
         // to get into our coordinate space
         if (theParent == this) {
             if (offsetFromChildToParent) {
-                rect.offset(descendant.mLeft - descendant.mScrollX,
-                        descendant.mTop - descendant.mScrollY);
+                rect.offset(-descendant.mScrollX, -descendant.mScrollY);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    rectF.set(rect);
+                    descendant.getMatrix().mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mLeft, descendant.mTop);
             } else {
-                rect.offset(descendant.mScrollX - descendant.mLeft,
-                        descendant.mScrollY - descendant.mTop);
+                rect.offset(-descendant.mLeft, -descendant.mTop);
+
+                if (!descendant.hasIdentityMatrix()) {
+                    descendant.getMatrix().invert(inverse);
+                    rectF.set(rect);
+                    inverse.mapRect(rectF);
+                    rectF.roundOut(rect);
+                }
+
+                rect.offset(descendant.mScrollX, descendant.mScrollY);
             }
         } else {
             throw new IllegalArgumentException("parameter must be a descendant of this view");
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f2b4fb3..9c19bf1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -17,6 +17,10 @@
 package android.view;
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 
 import android.Manifest;
 import android.animation.LayoutTransition;
@@ -79,6 +83,7 @@
 import android.widget.Scroller;
 
 import com.android.internal.R;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.policy.PhoneFallbackEventHandler;
 import com.android.internal.view.BaseSurfaceHolder;
@@ -140,6 +145,9 @@
      */
     static final int MAX_TRACKBALL_DELAY = 250;
 
+    private static final int RESIZE_MODE_FREEFORM = 0;
+    private static final int RESIZE_MODE_DOCKED_DIVIDER = 1;
+
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
     static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
@@ -224,8 +232,10 @@
     boolean mIsAnimating;
 
     private boolean mDragResizing;
+    private int mResizeMode;
     private int mCanvasOffsetX;
     private int mCanvasOffsetY;
+    private boolean mActivityRelaunched;
 
     CompatibilityInfo.Translator mTranslator;
 
@@ -384,6 +394,8 @@
         int localChanges;
     }
 
+    private String mTag = TAG;
+
     public ViewRootImpl(Context context, Display display) {
         mContext = context;
         mWindowSession = WindowManagerGlobal.getWindowSession();
@@ -510,6 +522,7 @@
                     mWindowAttributes.packageName = mBasePackageName;
                 }
                 attrs = mWindowAttributes;
+                setTag();
                 // Keep track of the actual window flags supplied by the client.
                 mClientWindowLayoutFlags = attrs.flags;
 
@@ -546,7 +559,7 @@
                     attrs.backup();
                     mTranslator.translateWindowLayout(attrs);
                 }
-                if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
+                if (DEBUG_LAYOUT) Log.d(mTag, "WindowLayout in setView:" + attrs);
 
                 if (!compatibilityInfo.supportsScreen()) {
                     attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -607,7 +620,7 @@
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingStableInsets.set(mAttachInfo.mStableInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
-                if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
+                if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
                     mAdded = false;
@@ -701,6 +714,13 @@
         }
     }
 
+    private void setTag() {
+        final String[] split = mWindowAttributes.getTitle().toString().split("\\.");
+        if (split.length > 0) {
+            mTag = TAG + "[" + split[split.length - 1] + "]";
+        }
+    }
+
     /** Whether the window is in local focus mode or not */
     private boolean isInLocalFocusMode() {
         return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
@@ -990,7 +1010,7 @@
     @Override
     public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
         checkThread();
-        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+        if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
 
         if (dirty == null) {
             invalidate();
@@ -1174,7 +1194,7 @@
 
     private boolean collectViewAttributes() {
         if (mAttachInfo.mRecomputeGlobalAttributes) {
-            //Log.i(TAG, "Computing view hierarchy attributes!");
+            //Log.i(mTag, "Computing view hierarchy attributes!");
             mAttachInfo.mRecomputeGlobalAttributes = false;
             boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
             mAttachInfo.mKeepScreenOn = false;
@@ -1215,7 +1235,7 @@
         int childHeightMeasureSpec;
         boolean windowSizeMayChange = false;
 
-        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
+        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
                 "Measuring " + host + " in display " + desiredWindowWidth
                 + "x" + desiredWindowHeight + "...");
 
@@ -1231,26 +1251,26 @@
             if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
                 baseSize = (int)mTmpValue.getDimension(packageMetrics);
             }
-            if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
+            if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize);
             if (baseSize != 0 && desiredWindowWidth > baseSize) {
                 childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
-                if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+                if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                         + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                 if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                     goodMeasure = true;
                 } else {
                     // Didn't fit in that size... try expanding a bit.
                     baseSize = (baseSize+desiredWindowWidth)/2;
-                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
                             + baseSize);
                     childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
-                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                             + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                     if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
-                        if (DEBUG_DIALOG) Log.v(TAG, "Good!");
+                        if (DEBUG_DIALOG) Log.v(mTag, "Good!");
                         goodMeasure = true;
                     }
                 }
@@ -1326,6 +1346,17 @@
         host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
     }
 
+    private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
+        return lp.type == TYPE_STATUS_BAR_PANEL
+                || lp.type == TYPE_INPUT_METHOD
+                || lp.type == TYPE_VOLUME_OVERLAY;
+    }
+
+    private int dipToPx(int dip) {
+        final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+        return (int) (displayMetrics.density * dip + 0.5f);
+    }
+
     private void performTraversals() {
         // cache mView since it is used so much below...
         final View host = mView;
@@ -1350,8 +1381,8 @@
         int desiredWindowHeight;
 
         final int viewVisibility = getHostVisibility();
-        boolean viewVisibilityChanged = mViewVisibility != viewVisibility
-                || mNewSurfaceNeeded;
+        final boolean viewVisibilityChanged = !mFirst
+                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
 
         WindowManager.LayoutParams params = null;
         if (mWindowAttributesChanged) {
@@ -1380,18 +1411,16 @@
             mFullRedrawNeeded = true;
             mLayoutRequested = true;
 
-            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
-                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+            if (shouldUseDisplaySize(lp)) {
                 // NOTE -- system code, won't try to do compat mode.
                 Point size = new Point();
                 mDisplay.getRealSize(size);
                 desiredWindowWidth = size.x;
                 desiredWindowHeight = size.y;
             } else {
-                DisplayMetrics packageMetrics =
-                    mView.getContext().getResources().getDisplayMetrics();
-                desiredWindowWidth = packageMetrics.widthPixels;
-                desiredWindowHeight = packageMetrics.heightPixels;
+                Configuration config = mContext.getResources().getConfiguration();
+                desiredWindowWidth = dipToPx(config.screenWidthDp);
+                desiredWindowHeight = dipToPx(config.screenHeightDp);
             }
 
             // We used to use the following condition to choose 32 bits drawing caches:
@@ -1401,7 +1430,6 @@
             mAttachInfo.mHasWindowFocus = false;
             mAttachInfo.mWindowVisibility = viewVisibility;
             mAttachInfo.mRecomputeGlobalAttributes = false;
-            viewVisibilityChanged = false;
             mLastConfiguration.setTo(host.getResources().getConfiguration());
             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
             // Set the layout direction if it has not been set before (inherit is the default)
@@ -1411,14 +1439,13 @@
             host.dispatchAttachedToWindow(mAttachInfo, 0);
             mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
             dispatchApplyInsets(host);
-            //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
+            //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
 
         } else {
             desiredWindowWidth = frame.width();
             desiredWindowHeight = frame.height();
             if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
-                if (DEBUG_ORIENTATION) Log.v(TAG,
-                        "View " + host + " resized to: " + frame);
+                if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
                 mFullRedrawNeeded = true;
                 mLayoutRequested = true;
                 windowSizeMayChange = true;
@@ -1471,7 +1498,7 @@
                 }
                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+                    if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
                             + mAttachInfo.mVisibleInsets);
                 }
                 if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
@@ -1481,17 +1508,16 @@
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
 
-                    if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
-                            || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+                    if (shouldUseDisplaySize(lp)) {
                         // NOTE -- system code, won't try to do compat mode.
                         Point size = new Point();
                         mDisplay.getRealSize(size);
                         desiredWindowWidth = size.x;
                         desiredWindowHeight = size.y;
                     } else {
-                        DisplayMetrics packageMetrics = res.getDisplayMetrics();
-                        desiredWindowWidth = packageMetrics.widthPixels;
-                        desiredWindowHeight = packageMetrics.heightPixels;
+                        Configuration config = res.getConfiguration();
+                        desiredWindowWidth = dipToPx(config.screenWidthDp);
+                        desiredWindowHeight = dipToPx(config.screenHeightDp);
                     }
                 }
             }
@@ -1572,12 +1598,17 @@
                         frame.width() < desiredWindowWidth && frame.width() != mWidth)
                 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                         frame.height() < desiredWindowHeight && frame.height() != mHeight));
-        windowShouldResize |= mDragResizing;
+        windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
 
         // If the backdrop frame doesn't equal to a frame, we are starting a resize operation, so
         // force it to be resized.
         windowShouldResize |= !mPendingBackDropFrame.equals(mWinFrame);
 
+        // If the activity was just relaunched, it might have unfrozen the task bounds (while
+        // relaunching), so we need to force a call into window manager to pick up the latest
+        // bounds.
+        windowShouldResize |= mActivityRelaunched;
+
         // Determine whether to compute insets.
         // If there are no inset listeners remaining then we may still need to compute
         // insets in case the old insets were non-empty and must be reset.
@@ -1616,7 +1647,7 @@
 
             try {
                 if (DEBUG_LAYOUT) {
-                    Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" +
+                    Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" +
                             host.getMeasuredHeight() + ", params=" + params);
                 }
 
@@ -1634,7 +1665,7 @@
                 final int surfaceGenerationId = mSurface.getGenerationId();
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
 
-                if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
+                if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
                         + " overscan=" + mPendingOverscanInsets.toShortString()
                         + " content=" + mPendingContentInsets.toShortString()
                         + " visible=" + mPendingVisibleInsets.toShortString()
@@ -1643,7 +1674,7 @@
                         + " surface=" + mSurface);
 
                 if (mPendingConfiguration.seq != 0) {
-                    if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
+                    if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
                             + mPendingConfiguration);
                     updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
                     mPendingConfiguration.seq = 0;
@@ -1662,19 +1693,19 @@
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
                 if (contentInsetsChanged) {
                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+                    if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: "
                             + mAttachInfo.mContentInsets);
                 }
                 if (overscanInsetsChanged) {
                     mAttachInfo.mOverscanInsets.set(mPendingOverscanInsets);
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Overscan insets changing to: "
+                    if (DEBUG_LAYOUT) Log.v(mTag, "Overscan insets changing to: "
                             + mAttachInfo.mOverscanInsets);
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
                 }
                 if (stableInsetsChanged) {
                     mAttachInfo.mStableInsets.set(mPendingStableInsets);
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Decor insets changing to: "
+                    if (DEBUG_LAYOUT) Log.v(mTag, "Decor insets changing to: "
                             + mAttachInfo.mStableInsets);
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
@@ -1691,7 +1722,7 @@
                 }
                 if (visibleInsetsChanged) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+                    if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
                             + mAttachInfo.mVisibleInsets);
                 }
 
@@ -1766,11 +1797,17 @@
                     }
                 }
 
-                final boolean dragResizing = (relayoutResult
-                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
+                final boolean freeformResizing = (relayoutResult
+                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
+                final boolean dockedResizing = (relayoutResult
+                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
+                final boolean dragResizing = freeformResizing || dockedResizing;
                 if (mDragResizing != dragResizing) {
                     if (dragResizing) {
                         startDragResizing(mPendingBackDropFrame);
+                        mResizeMode = freeformResizing
+                                ? RESIZE_MODE_FREEFORM
+                                : RESIZE_MODE_DOCKED_DIVIDER;
                     } else {
                         // We shouldn't come here, but if we come we should end the resize.
                         endDragResizing();
@@ -1872,7 +1909,7 @@
                     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
 
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
+                    if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                             + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                             + " mHeight=" + mHeight
                             + " measuredHeight=" + host.getMeasuredHeight()
@@ -1902,7 +1939,7 @@
                     }
 
                     if (measureAgain) {
-                        if (DEBUG_LAYOUT) Log.v(TAG,
+                        if (DEBUG_LAYOUT) Log.v(mTag,
                                 "And hey let's measure once more: width=" + width
                                 + " height=" + height);
                         performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
@@ -1917,29 +1954,7 @@
             // in the attach info. We translate only the window frame since on window move
             // the window manager tells us only for the new frame but the insets are the
             // same and we do not want to translate them more than once.
-
-            // TODO: Well, we are checking whether the frame has changed similarly
-            // to how this is done for the insets. This is however incorrect since
-            // the insets and the frame are translated. For example, the old frame
-            // was (1, 1 - 1, 1) and was translated to say (2, 2 - 2, 2), now the new
-            // reported frame is (2, 2 - 2, 2) which implies no change but this is not
-            // true since we are comparing a not translated value to a translated one.
-            // This scenario is rare but we may want to fix that.
-
-            final boolean windowMoved = (mAttachInfo.mWindowLeft != frame.left
-                    || mAttachInfo.mWindowTop != frame.top);
-            if (windowMoved) {
-                if (mTranslator != null) {
-                    mTranslator.translateRectInScreenToAppWinFrame(frame);
-                }
-                mAttachInfo.mWindowLeft = frame.left;
-                mAttachInfo.mWindowTop = frame.top;
-
-                // Update the light position for the new window offsets.
-                if (mAttachInfo.mHardwareRenderer != null) {
-                    mAttachInfo.mHardwareRenderer.setLightCenter(mAttachInfo);
-                }
-            }
+            maybeHandleWindowMove(frame);
         }
 
         final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -2024,15 +2039,15 @@
 
         if (mFirst) {
             // handle first focus request
-            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
+            if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
                     + mView.hasFocus());
             if (mView != null) {
                 if (!mView.hasFocus()) {
                     mView.requestFocus(View.FOCUS_FORWARD);
-                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
+                    if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: requested focused view="
                             + mView.findFocus());
                 } else {
-                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
+                    if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: existing focused view="
                             + mView.findFocus());
                 }
             }
@@ -2054,6 +2069,7 @@
         mFirst = false;
         mWillDrawSoon = false;
         mNewSurfaceNeeded = false;
+        mActivityRelaunched = false;
         mViewVisibility = viewVisibility;
         mHadWindowFocus = hasWindowFocus;
 
@@ -2103,12 +2119,37 @@
         mIsInTraversal = false;
     }
 
+    private void maybeHandleWindowMove(Rect frame) {
+
+        // TODO: Well, we are checking whether the frame has changed similarly
+        // to how this is done for the insets. This is however incorrect since
+        // the insets and the frame are translated. For example, the old frame
+        // was (1, 1 - 1, 1) and was translated to say (2, 2 - 2, 2), now the new
+        // reported frame is (2, 2 - 2, 2) which implies no change but this is not
+        // true since we are comparing a not translated value to a translated one.
+        // This scenario is rare but we may want to fix that.
+
+        final boolean windowMoved = mAttachInfo.mWindowLeft != frame.left
+                || mAttachInfo.mWindowTop != frame.top;
+        if (windowMoved) {
+            if (mTranslator != null) {
+                mTranslator.translateRectInScreenToAppWinFrame(frame);
+            }
+            mAttachInfo.mWindowLeft = frame.left;
+            mAttachInfo.mWindowTop = frame.top;
+
+            // Update the light position for the new window offsets.
+            if (mAttachInfo.mHardwareRenderer != null) {
+                mAttachInfo.mHardwareRenderer.setLightCenter(mAttachInfo);
+            }
+        }
+    }
     private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
-        Log.e(TAG, "OutOfResourcesException initializing HW surface", e);
+        Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
         try {
             if (!mWindowSession.outOfMemory(mWindow) &&
                     Process.myUid() != Process.SYSTEM_UID) {
-                Slog.w(TAG, "No processes killed for memory; killing self");
+                Slog.w(mTag, "No processes killed for memory; killing self");
                 Process.killProcess(Process.myPid());
             }
         } catch (RemoteException ex) {
@@ -2184,7 +2225,7 @@
 
         final View host = mView;
         if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
-            Log.v(TAG, "Laying out " + host + " to (" +
+            Log.v(mTag, "Laying out " + host + " to (" +
                     host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
         }
 
@@ -2427,11 +2468,11 @@
             String thisHash = Integer.toHexString(System.identityHashCode(this));
             long frameTime = nowTime - mFpsPrevTime;
             long totalTime = nowTime - mFpsStartTime;
-            Log.v(TAG, "0x" + thisHash + "\tFrame time:\t" + frameTime);
+            Log.v(mTag, "0x" + thisHash + "\tFrame time:\t" + frameTime);
             mFpsPrevTime = nowTime;
             if (totalTime > 1000) {
                 float fps = (float) mFpsNumFrames * 1000 / totalTime;
-                Log.v(TAG, "0x" + thisHash + "\tFPS:\t" + fps);
+                Log.v(mTag, "0x" + thisHash + "\tFPS:\t" + fps);
                 mFpsStartTime = nowTime;
                 mFpsNumFrames = 0;
             }
@@ -2473,7 +2514,7 @@
                 try {
                     mWindowDrawCountDown.await();
                 } catch (InterruptedException e) {
-                    Log.e(TAG, "Window redraw count down interruped!");
+                    Log.e(mTag, "Window redraw count down interruped!");
                 }
                 mWindowDrawCountDown = null;
             }
@@ -2483,7 +2524,7 @@
             }
 
             if (LOCAL_LOGV) {
-                Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
+                Log.v(mTag, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
             }
             if (mSurfaceHolder != null && mSurface.isValid()) {
                 mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
@@ -2566,7 +2607,7 @@
         }
 
         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
-            Log.v(TAG, "Draw " + mView + "/"
+            Log.v(mTag, "Draw " + mView + "/"
                     + mWindowAttributes.getTitle()
                     + ": dirty={" + dirty.left + "," + dirty.top
                     + "," + dirty.right + "," + dirty.bottom + "} surface="
@@ -2700,7 +2741,7 @@
             handleOutOfResourcesException(e);
             return false;
         } catch (IllegalArgumentException e) {
-            Log.e(TAG, "Could not lock surface", e);
+            Log.e(mTag, "Could not lock surface", e);
             // Don't assume this is due to out of memory, it could be
             // something else, and if it is something else then we could
             // kill stuff (or ourself) for no reason.
@@ -2710,7 +2751,7 @@
 
         try {
             if (DEBUG_ORIENTATION || DEBUG_DRAW) {
-                Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
+                Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                         + canvas.getWidth() + ", h=" + canvas.getHeight());
                 //canvas.drawARGB(255, 255, 0, 0);
             }
@@ -2733,7 +2774,7 @@
 
             if (DEBUG_DRAW) {
                 Context cxt = mView.getContext();
-                Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
+                Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
                         ", metrics=" + cxt.getResources().getDisplayMetrics() +
                         ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
             }
@@ -2758,14 +2799,14 @@
             try {
                 surface.unlockCanvasAndPost(canvas);
             } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Could not unlock surface", e);
+                Log.e(mTag, "Could not unlock surface", e);
                 mLayoutRequested = true;    // ask wm for a new surface next time.
                 //noinspection ReturnInsideFinallyBlock
                 return false;
             }
 
             if (LOCAL_LOGV) {
-                Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+                Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
             }
         }
         return true;
@@ -2870,14 +2911,14 @@
                 // view is visible.
                 rectangle = null;
             }
-            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
+            if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Eval scroll: focus=" + focus
                     + " rectangle=" + rectangle + " ci=" + ci
                     + " vi=" + vi);
             if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
                 // Optimization: if the focus hasn't changed since last
                 // time, and no layout has happened, then just leave things
                 // as they are.
-                if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
+                if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Keeping scroll y="
                         + mScrollY + " vi=" + vi.toShortString());
             } else {
                 // We need to determine if the currently focused view is
@@ -2885,51 +2926,51 @@
                 // a pan so it can be seen.
                 mLastScrolledFocus = new WeakReference<View>(focus);
                 mScrollMayChange = false;
-                if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
+                if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Need to scroll?");
                 // Try to find the rectangle from the focus view.
                 if (focus.getGlobalVisibleRect(mVisRect, null)) {
-                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
+                    if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Root w="
                             + mView.getWidth() + " h=" + mView.getHeight()
                             + " ci=" + ci.toShortString()
                             + " vi=" + vi.toShortString());
                     if (rectangle == null) {
                         focus.getFocusedRect(mTempRect);
-                        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
+                        if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Focus " + focus
                                 + ": focusRect=" + mTempRect.toShortString());
                         if (mView instanceof ViewGroup) {
                             ((ViewGroup) mView).offsetDescendantRectToMyCoords(
                                     focus, mTempRect);
                         }
-                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                        if (DEBUG_INPUT_RESIZE) Log.v(mTag,
                                 "Focus in window: focusRect="
                                 + mTempRect.toShortString()
                                 + " visRect=" + mVisRect.toShortString());
                     } else {
                         mTempRect.set(rectangle);
-                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                        if (DEBUG_INPUT_RESIZE) Log.v(mTag,
                                 "Request scroll to rect: "
                                 + mTempRect.toShortString()
                                 + " visRect=" + mVisRect.toShortString());
                     }
                     if (mTempRect.intersect(mVisRect)) {
-                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                        if (DEBUG_INPUT_RESIZE) Log.v(mTag,
                                 "Focus window visible rect: "
                                 + mTempRect.toShortString());
                         if (mTempRect.height() >
                                 (mView.getHeight()-vi.top-vi.bottom)) {
                             // If the focus simply is not going to fit, then
                             // best is probably just to leave things as-is.
-                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                            if (DEBUG_INPUT_RESIZE) Log.v(mTag,
                                     "Too tall; leaving scrollY=" + scrollY);
                         } else if ((mTempRect.top-scrollY) < vi.top) {
                             scrollY -= vi.top - (mTempRect.top-scrollY);
-                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                            if (DEBUG_INPUT_RESIZE) Log.v(mTag,
                                     "Top covered; scrollY=" + scrollY);
                         } else if ((mTempRect.bottom-scrollY)
                                 > (mView.getHeight()-vi.bottom)) {
                             scrollY += (mTempRect.bottom-scrollY)
                                     - (mView.getHeight()-vi.bottom);
-                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                            if (DEBUG_INPUT_RESIZE) Log.v(mTag,
                                     "Bottom covered; scrollY=" + scrollY);
                         }
                         handled = true;
@@ -2939,7 +2980,7 @@
         }
 
         if (scrollY != mScrollY) {
-            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
+            if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Pan scroll changed: old="
                     + mScrollY + " , new=" + scrollY);
             if (!immediate) {
                 if (mScroller == null) {
@@ -3018,7 +3059,7 @@
 
     void setPointerCapture(View view) {
         if (!mAttachInfo.mHasWindowFocus) {
-            Log.w(TAG, "Can't set capture if it's not focused.");
+            Log.w(mTag, "Can't set capture if it's not focused.");
             return;
         }
         if (mCapturingView == view) {
@@ -3044,7 +3085,7 @@
     @Override
     public void requestChildFocus(View child, View focused) {
         if (DEBUG_INPUT_RESIZE) {
-            Log.v(TAG, "Request child focus: focus now " + focused);
+            Log.v(mTag, "Request child focus: focus now " + focused);
         }
         checkThread();
         scheduleTraversals();
@@ -3053,7 +3094,7 @@
     @Override
     public void clearChildFocus(View child) {
         if (DEBUG_INPUT_RESIZE) {
-            Log.v(TAG, "Clearing child focus");
+            Log.v(mTag, "Clearing child focus");
         }
         checkThread();
         scheduleTraversals();
@@ -3152,7 +3193,7 @@
     }
 
     void updateConfiguration(Configuration config, boolean force) {
-        if (DEBUG_CONFIGURATION) Log.v(TAG,
+        if (DEBUG_CONFIGURATION) Log.v(mTag,
                 "Applying new config to window "
                 + mWindowAttributes.getTitle()
                 + ": " + config);
@@ -3232,6 +3273,7 @@
     private final static int MSG_WINDOW_MOVED = 23;
     private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24;
     private final static int MSG_DISPATCH_WINDOW_SHOWN = 25;
+    private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -3315,6 +3357,7 @@
                         && mPendingStableInsets.equals(args.arg6)
                         && mPendingVisibleInsets.equals(args.arg3)
                         && mPendingOutsets.equals(args.arg7)
+                        && mPendingBackDropFrame.equals(args.arg8)
                         && args.arg4 == null) {
                     break;
                 }
@@ -3362,10 +3405,19 @@
 
                     mPendingBackDropFrame.set(mWinFrame);
 
-                    if (mView != null) {
-                        forceLayout(mView);
+                    // Suppress layouts during resizing - a correct layout will happen when resizing
+                    // is done, and this just increases system load.
+                    boolean isDockedDivider = mWindowAttributes.type == TYPE_DOCK_DIVIDER;
+                    boolean suppress = (mDragResizing && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER)
+                            || isDockedDivider;
+                    if (!suppress) {
+                        if (mView != null) {
+                            forceLayout(mView);
+                        }
+                        requestLayout();
+                    } else {
+                        maybeHandleWindowMove(mWinFrame);
                     }
-                    requestLayout();
                 }
                 break;
             case MSG_WINDOW_FOCUS_CHANGED: {
@@ -3387,10 +3439,10 @@
                                 mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                                         mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
                             } catch (OutOfResourcesException e) {
-                                Log.e(TAG, "OutOfResourcesException locking surface", e);
+                                Log.e(mTag, "OutOfResourcesException locking surface", e);
                                 try {
                                     if (!mWindowSession.outOfMemory(mWindow)) {
-                                        Slog.w(TAG, "No processes killed for memory; killing self");
+                                        Slog.w(mTag, "No processes killed for memory; killing self");
                                         Process.killProcess(Process.myPid());
                                     }
                                 } catch (RemoteException ex) {
@@ -3507,7 +3559,11 @@
             } break;
             case MSG_DISPATCH_WINDOW_SHOWN: {
                 handleDispatchWindowShown();
-            }
+            } break;
+            case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
+                IResultReceiver receiver = (IResultReceiver) msg.obj;
+                handleRequestKeyboardShortcuts(receiver);
+            } break;
             }
         }
     }
@@ -3713,7 +3769,7 @@
          */
         protected void onDeliverToNext(QueuedInputEvent q) {
             if (DEBUG_INPUT_STAGES) {
-                Log.v(TAG, "Done with " + getClass().getSimpleName() + ". " + q);
+                Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
             }
             if (mNext != null) {
                 mNext.deliver(q);
@@ -3724,7 +3780,7 @@
 
         protected boolean shouldDropInputEvent(QueuedInputEvent q) {
             if (mView == null || !mAdded) {
-                Slog.w(TAG, "Dropping event due to root view being removed: " + q.mEvent);
+                Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
                 return true;
             } else if ((!mAttachInfo.mHasWindowFocus
                     && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) || mStopped
@@ -3735,12 +3791,12 @@
                 if (isTerminalInputEvent(q.mEvent)) {
                     // Don't drop terminal input events, however mark them as canceled.
                     q.mEvent.cancel();
-                    Slog.w(TAG, "Cancelling event due to no window focus: " + q.mEvent);
+                    Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
                     return false;
                 }
 
                 // Drop non-terminal input events.
-                Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
                 return true;
             }
             return false;
@@ -3980,7 +4036,7 @@
                 InputMethodManager imm = InputMethodManager.peekInstance();
                 if (imm != null) {
                     final InputEvent event = q.mEvent;
-                    if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
+                    if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
                     int result = imm.dispatchInputEvent(event, q, this, mHandler);
                     if (result == InputMethodManager.DISPATCH_HANDLED) {
                         return FINISH_HANDLED;
@@ -4412,7 +4468,7 @@
                     break;
             }
 
-            if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step="
+            if (DEBUG_TRACKBALL) Log.v(mTag, "TB X=" + mX.position + " step="
                     + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
                     + " move=" + event.getX()
                     + " / Y=" + mY.position + " step="
@@ -4451,11 +4507,11 @@
             if (keycode != 0) {
                 if (movement < 0) movement = -movement;
                 int accelMovement = (int)(movement * accel);
-                if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+                if (DEBUG_TRACKBALL) Log.v(mTag, "Move: movement=" + movement
                         + " accelMovement=" + accelMovement
                         + " accel=" + accel);
                 if (accelMovement > movement) {
-                    if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+                    if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: "
                             + keycode);
                     movement--;
                     int repeatCount = accelMovement - movement;
@@ -4465,7 +4521,7 @@
                             InputDevice.SOURCE_KEYBOARD));
                 }
                 while (movement > 0) {
-                    if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+                    if (DEBUG_TRACKBALL) Log.v(mTag, "Delivering fake DPAD: "
                             + keycode);
                     movement--;
                     curTime = SystemClock.uptimeMillis();
@@ -4707,7 +4763,7 @@
                 update(event, true);
                 break;
             default:
-                Log.w(TAG, "Unexpected action: " + event.getActionMasked());
+                Log.w(mTag, "Unexpected action: " + event.getActionMasked());
             }
         }
 
@@ -5346,7 +5402,7 @@
                             mWindowSession.dragRecipientEntered(mWindow);
                         }
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to note drag target change");
+                        Slog.e(mTag, "Unable to note drag target change");
                     }
                 }
 
@@ -5354,10 +5410,10 @@
                 if (what == DragEvent.ACTION_DROP) {
                     mDragDescription = null;
                     try {
-                        Log.i(TAG, "Reporting drop result: " + result);
+                        Log.i(mTag, "Reporting drop result: " + result);
                         mWindowSession.reportDropResult(mWindow, result);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "Unable to report drop result");
+                        Log.e(mTag, "Unable to report drop result");
                     }
                 }
 
@@ -5400,6 +5456,19 @@
         mAttachInfo.mTreeObserver.dispatchOnWindowShown();
     }
 
+    public void handleRequestKeyboardShortcuts(IResultReceiver receiver) {
+        Bundle data = new Bundle();
+        ArrayList<KeyboardShortcutGroup> list = new ArrayList<>();
+        if (mView != null) {
+            mView.requestKeyboardShortcuts(list);
+        }
+        data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list);
+        try {
+            receiver.send(0, data);
+        } catch (RemoteException e) {
+        }
+    }
+
     public void getLastTouchPoint(Point outLocation) {
         outLocation.x = (int) mLastTouchPoint.x;
         outLocation.y = (int) mLastTouchPoint.y;
@@ -5443,14 +5512,14 @@
             mTranslator.translateWindowLayout(params);
         }
         if (params != null) {
-            if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
+            if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
         }
         mPendingConfiguration.seq = 0;
-        //Log.d(TAG, ">>>>>> CALLING relayout");
+        //Log.d(mTag, ">>>>>> CALLING relayout");
         if (params != null && mOrigWindowType != params.type) {
             // For compatibility with old apps, don't crash here.
             if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-                Slog.w(TAG, "Window type can not be changed after "
+                Slog.w(mTag, "Window type can not be changed after "
                         + "the window is added; ignoring change of " + mView);
                 params.type = mOrigWindowType;
             }
@@ -5461,8 +5530,9 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingStableInsets, mPendingOutsets, mPendingConfiguration, mSurface);
-        //Log.d(TAG, "<<<<<< BACK FROM relayout");
+                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
+                mSurface);
+        //Log.d(mTag, "<<<<<< BACK FROM relayout");
         if (restore) {
             params.restore();
         }
@@ -5509,7 +5579,7 @@
             }
         } catch (IllegalStateException e) {
             // Exception thrown by getAudioManager() when mView is null
-            Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
+            Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e);
             e.printStackTrace();
         }
     }
@@ -5632,7 +5702,7 @@
         if (!mIsDrawing) {
             destroyHardwareRenderer();
         } else {
-            Log.e(TAG, "Attempting to destroy the window while drawing!\n" +
+            Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                     "  window=" + this + ", title=" + mWindowAttributes.getTitle());
         }
         mHandler.sendEmptyMessage(MSG_DIE);
@@ -5641,7 +5711,7 @@
 
     void doDie() {
         checkThread();
-        if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
+        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
         synchronized (this) {
             if (mRemoved) {
                 return;
@@ -5734,7 +5804,7 @@
     public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
             Configuration newConfig, Rect backDropFrame) {
-        if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString()
+        if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw
@@ -5772,7 +5842,7 @@
     }
 
     public void dispatchMoved(int newX, int newY) {
-        if (DEBUG_LAYOUT) Log.v(TAG, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
+        if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
         if (mTranslator != null) {
             PointF point = new PointF(newX, newY);
             mTranslator.translatePointInScreenToAppWindow(point);
@@ -6329,6 +6399,10 @@
         }
     }
 
+    public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver) {
+        mHandler.obtainMessage(MSG_REQUEST_KEYBOARD_SHORTCUTS, receiver).sendToTarget();
+    }
+
     /**
      * Post a callback to send a
      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
@@ -6689,7 +6763,7 @@
     }
 
     void changeCanvasOpacity(boolean opaque) {
-        Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
+        Log.d(mTag, "changeCanvasOpacity: opaque=" + opaque);
         if (mAttachInfo.mHardwareRenderer != null) {
             mAttachInfo.mHardwareRenderer.setOpaque(opaque);
         }
@@ -6902,6 +6976,14 @@
                 viewAncestor.dispatchWindowShown();
             }
         }
+
+        @Override
+        public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+          ViewRootImpl viewAncestor = mViewAncestor.get();
+          if (viewAncestor != null) {
+            viewAncestor.dispatchRequestKeyboardShortcuts(receiver);
+          }
+        }
     }
 
     public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
@@ -6975,6 +7057,15 @@
     }
 
     /**
+     * Tells this instance that its corresponding activity has just relaunched. In this case, we
+     * need to force a relayout of the window to make sure we get the correct bounds from window
+     * manager.
+     */
+    public void reportActivityRelaunched() {
+        mActivityRelaunched = true;
+    }
+
+    /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
      */
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index d7a98ab..dfe0cc7 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -42,6 +42,8 @@
 import android.transition.TransitionManager;
 import android.view.accessibility.AccessibilityEvent;
 
+import java.util.List;
+
 /**
  * Abstract base class for a top-level window look and behavior policy.  An
  * instance of this class should be used as the top-level view added to the
@@ -556,6 +558,15 @@
          * @param mode The mode that was just finished.
          */
         public void onActionModeFinished(ActionMode mode);
+
+        /**
+         * Called when Keyboard Shortcuts are requested for the current window.
+         *
+         * @param data The data list to populate with shortcuts.
+         * @param menu The current menu, which may be null.
+         */
+        public void onProvideKeyboardShortcuts(
+                List<KeyboardShortcutGroup> data, @Nullable Menu menu);
     }
 
     /** @hide */
@@ -571,14 +582,11 @@
     /** @hide */
     public interface WindowControllerCallback {
         /**
-         * Called to move the window and its activity/task to a different stack container.
-         * For example, a window can move between
-         * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack and
-         * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} stack.
-         *
-         * @param stackId stack Id to change to.
+         * Moves the activity from
+         * {@link android.app.ActivityManager.StackId#FREEFORM_WORKSPACE_STACK_ID} to
+         * {@link android.app.ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} stack.
          */
-        void changeWindowStack(int stackId) throws RemoteException;
+        void exitFreeformMode() throws RemoteException;
 
         /** Returns the current stack Id for the window. */
         int getWindowStackId() throws RemoteException;
@@ -2112,4 +2120,10 @@
      * @hide
      */
     public abstract void onMultiWindowModeChanged();
+
+    /**
+     * Called when the activity just relaunched.
+     * @hide
+     */
+    public abstract void reportActivityRelaunched();
 }
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 8ce1f8c..bed74e9 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -19,6 +19,8 @@
 
 import android.view.accessibility.AccessibilityEvent;
 
+import java.util.List;
+
 /**
  * A simple decorator stub for Window.Callback that passes through any calls
  * to the wrapped instance as a base implementation. Call super.foo() to call into
@@ -150,5 +152,10 @@
     public void onActionModeFinished(ActionMode mode) {
         mWrapped.onActionModeFinished(mode);
     }
+
+    @Override
+    public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+        mWrapped.onProvideKeyboardShortcuts(data, menu);
+    }
 }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 251f4c8..772eeec 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -29,6 +29,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.List;
+
 
 /**
  * The interface that apps use to talk to the window manager.
@@ -118,6 +120,34 @@
      */
     public void removeViewImmediate(View view);
 
+    /**
+     * Used to asynchronously request Keyboard Shortcuts from the focused window.
+     *
+     * @hide
+     */
+    public interface KeyboardShortcutsReceiver {
+        /**
+         * Callback used when the focused window keyboard shortcuts are ready to be displayed.
+         *
+         * @param result The keyboard shortcuts to be displayed.
+         */
+        void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result);
+    }
+
+    /**
+     * @hide
+     */
+    public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
+
+    /**
+     * Request for keyboard shortcuts to be retrieved asynchronously.
+     *
+     * @param receiver The callback to be triggered when the result is ready.
+     *
+     * @hide
+     */
+    public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver);
+
     public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 8c68e92..1530b47 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -30,6 +30,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.inputmethod.InputMethodManager;
+
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.FileDescriptor;
@@ -70,16 +71,23 @@
     public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
 
     /**
+     * The window is being resized by dragging on the docked divider. The client should render
+     * at (0, 0) and extend its background to the background frame passed into
+     * {@link IWindow#resized}.
+     */
+    public static final int RELAYOUT_RES_DRAG_RESIZING_DOCKED = 0x8;
+
+    /**
      * The window is being resized by dragging one of the window corners,
      * in this case the surface would be fullscreen-sized. The client should
      * render to the actual frame location (instead of (0,curScrollY)).
      */
-    public static final int RELAYOUT_RES_DRAG_RESIZING = 0x8;
+    public static final int RELAYOUT_RES_DRAG_RESIZING_FREEFORM = 0x10;
 
     /**
      * The window manager has changed the size of the surface from the last call.
      */
-    public static final int RELAYOUT_RES_SURFACE_RESIZED = 0x10;
+    public static final int RELAYOUT_RES_SURFACE_RESIZED = 0x20;
 
     /**
      * Flag for relayout: the client will be later giving
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 98e9f54..6e11671 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -17,7 +17,15 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.R;
+
+import java.util.List;
 
 /**
  * Provides low-level communication with the system window manager for
@@ -117,6 +125,23 @@
     }
 
     @Override
+    public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver) {
+        IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) throws RemoteException {
+                List<KeyboardShortcutGroup> result =
+                        resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
+                receiver.onKeyboardShortcutsReceived(result);
+            }
+        };
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                .requestAppKeyboardShortcuts(resultReceiver);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
     public Display getDefaultDisplay() {
         return mDisplay;
     }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 01bfbb5..4a1142f 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -623,14 +623,16 @@
      * decorations that can never be removed.  That is, system bar or
      * button bar.
      */
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation);
+    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation,
+            int uiMode);
 
     /**
      * Return the display height available after excluding any screen
      * decorations that can never be removed.  That is, system bar or
      * button bar.
      */
-    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation);
+    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation,
+            int uiMode);
 
     /**
      * Return the available screen width that we should report for the
@@ -638,7 +640,8 @@
      * {@link #getNonDecorDisplayWidth(int, int, int)}; it may be smaller than
      * that to account for more transient decoration like a status bar.
      */
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation);
+    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation,
+            int uiMode);
 
     /**
      * Return the available screen height that we should report for the
@@ -646,7 +649,8 @@
      * {@link #getNonDecorDisplayHeight(int, int, int)}; it may be smaller than
      * that to account for more transient decoration like a status bar.
      */
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation);
+    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation,
+            int uiMode);
 
     /**
      * Return whether the given window is forcibly hiding all windows except windows with
@@ -861,11 +865,11 @@
      * @param isDefaultDisplay true if window is on {@link Display#DEFAULT_DISPLAY}.
      * @param displayWidth The current full width of the screen.
      * @param displayHeight The current full height of the screen.
-     * @param displayRotation The current rotation being applied to the base
-     * window.
+     * @param displayRotation The current rotation being applied to the base window.
+     * @param uiMode The current uiMode in configuration.
      */
     public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
-                              int displayRotation);
+                              int displayRotation, int uiMode);
 
     /**
      * Returns the bottom-most layer of the system decor, above which no policy decor should
@@ -1324,4 +1328,15 @@
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+
+    /**
+     * Calculates the stable insets without running a layout.
+     *
+     * @param displayRotation the current display rotation
+     * @param displayWidth the current display width
+     * @param displayHeight the current display height
+     * @param outInsets the insets to return
+     */
+    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            Rect outInsets);
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index b6570cc..9e79057 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -58,5 +58,5 @@
     void temporaryEnableAccessibilityStateUntilKeyguardRemoved(in ComponentName service,
             boolean touchExplorationEnabled);
 
-    IBinder getWindowToken(int windowId);
+    IBinder getWindowToken(int windowId, int userId);
 }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index e0dbe2f..1536c29 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -850,7 +850,7 @@
             normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
         }
 
-        final boolean expired = normalizedTime >= 1.0f;
+        final boolean expired = normalizedTime >= 1.0f || isCanceled();
         mMore = !expired;
 
         if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
@@ -875,7 +875,7 @@
         }
 
         if (expired) {
-            if (mRepeatCount == mRepeated) {
+            if (mRepeatCount == mRepeated || isCanceled()) {
                 if (!mEnded) {
                     mEnded = true;
                     guard.close();
@@ -905,6 +905,10 @@
         return mMore;
     }
 
+    private boolean isCanceled() {
+        return mStartTime == Long.MIN_VALUE;
+    }
+
     private void fireAnimationStart() {
         if (mListener != null) {
             if (mListenerHandler == null) mListener.onAnimationStart(this);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index ba5d6c2..bdf89e9 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -195,16 +195,22 @@
     public boolean commitText(CharSequence text, int newCursorPosition) {
         if (DEBUG) Log.v(TAG, "commitText " + text);
         replaceText(text, newCursorPosition, false);
-        mIMM.notifyUserAction();
         sendCurrentText();
         return true;
     }
 
     /**
-     * The default implementation performs the deletion around the current
-     * selection position of the editable text.
-     * @param beforeLength
-     * @param afterLength
+     * The default implementation performs the deletion around the current selection position of the
+     * editable text.
+     *
+     * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the beginning of the
+     *        text and the cursor, then this method does not fail but deletes all the characters in
+     *        that range.
+     * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the cursor and
+     *        the end of the text, then this method does not fail but deletes all the characters in
+     *        that range.
      */
     public boolean deleteSurroundingText(int beforeLength, int afterLength) {
         if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
@@ -213,7 +219,7 @@
         if (content == null) return false;
 
         beginBatchEdit();
-        
+
         int a = Selection.getSelectionStart(content);
         int b = Selection.getSelectionEnd(content);
 
@@ -253,9 +259,9 @@
 
             content.delete(b, end);
         }
-        
+
         endBatchEdit();
-        
+
         return true;
     }
 
@@ -443,7 +449,6 @@
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         if (DEBUG) Log.v(TAG, "setComposingText " + text);
         replaceText(text, newCursorPosition, true);
-        mIMM.notifyUserAction();
         return true;
     }
 
@@ -516,29 +521,17 @@
      * attached to the input connection's view.
      */
     public boolean sendKeyEvent(KeyEvent event) {
-        synchronized (mIMM.mH) {
-            ViewRootImpl viewRootImpl = mTargetView != null ? mTargetView.getViewRootImpl() : null;
-            if (viewRootImpl == null) {
-                if (mIMM.mServedView != null) {
-                    viewRootImpl = mIMM.mServedView.getViewRootImpl();
-                }
-            }
-            if (viewRootImpl != null) {
-                viewRootImpl.dispatchKeyFromIme(event);
-            }
-        }
-        mIMM.notifyUserAction();
+        mIMM.dispatchKeyEventFromInputMethod(mTargetView, event);
         return false;
     }
-    
+
     /**
      * Updates InputMethodManager with the current fullscreen mode.
      */
     public boolean reportFullscreenMode(boolean enabled) {
-        mIMM.setFullscreenMode(enabled);
         return true;
     }
-    
+
     private void sendCurrentText() {
         if (!mDummyMode) {
             return;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index ff992d3..be7bc14 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -335,12 +335,15 @@
      * but be careful to wait until the batch edit is over if one is
      * in progress.</p>
      *
-     * @param beforeLength The number of characters to be deleted before the
-     *        current cursor position.
-     * @param afterLength The number of characters to be deleted after the
-     *        current cursor position.
-     * @return true on success, false if the input connection is no longer
-     * valid.
+     * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the beginning of the
+     *        text and the cursor, then this method does not fail but deletes all the characters in
+     *        that range.
+     * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+     *        If this is greater than the number of existing characters between the cursor and
+     *        the end of the text, then this method does not fail but deletes all the characters in
+     *        that range.
+     * @return true on success, false if the input connection is no longer valid.
      */
     public boolean deleteSurroundingText(int beforeLength, int afterLength);
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5e07347..9647345 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -25,6 +25,8 @@
 import com.android.internal.view.InputBindResult;
 import com.android.internal.view.InputMethodClient;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.graphics.Rect;
@@ -527,7 +529,7 @@
             }
         }
     }
-    
+
     private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
         private final InputMethodManager mParentInputMethodManager;
         private boolean mActive;
@@ -549,13 +551,23 @@
         }
 
         @Override
+        protected void onUserAction() {
+            mParentInputMethodManager.notifyUserAction();
+        }
+
+        @Override
+        protected void onReportFullscreenMode(boolean enabled) {
+            mParentInputMethodManager.setFullscreenMode(enabled);
+        }
+
+        @Override
         public String toString() {
             return "ControlledInputConnectionWrapper{mActive=" + mActive
                     + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
                     + "}";
         }
     }
-    
+
     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
         @Override
         protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -1813,6 +1825,34 @@
         return DISPATCH_NOT_HANDLED;
     }
 
+    /**
+     * Provides the default implementation of {@link InputConnection#sendKeyEvent(KeyEvent)}, which
+     * is expected to dispatch an keyboard event sent from the IME to an appropriate event target
+     * depending on the given {@link View} and the current focus state.
+     *
+     * <p>CAUTION: This method is provided only for the situation where
+     * {@link InputConnection#sendKeyEvent(KeyEvent)} needs to be implemented without relying on
+     * {@link BaseInputConnection}. Do not use this API for anything else.</p>
+     *
+     * @param targetView the default target view. If {@code null} is specified, then this method
+     * tries to find a good event target based on the current focus state.
+     * @param event the key event to be dispatched.
+     */
+    public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
+            @NonNull KeyEvent event) {
+        synchronized (mH) {
+            ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
+            if (viewRootImpl == null) {
+                if (mServedView != null) {
+                    viewRootImpl = mServedView.getViewRootImpl();
+                }
+            }
+            if (viewRootImpl != null) {
+                viewRootImpl.dispatchKeyFromIme(event);
+            }
+        }
+    }
+
     // Must be called on the main looper
     void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
         final boolean handled;
diff --git a/core/java/android/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java
new file mode 100644
index 0000000..a6d7b4a
--- /dev/null
+++ b/core/java/android/webkit/TokenBindingService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+
+import java.security.KeyPair;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * Enables the token binding procotol, and provides access to the keys. See
+ * https://tools.ietf.org/html/draft-ietf-tokbind-protocol-03
+ *
+ * All methods are required to be called on the UI thread where WebView is
+ * attached to the View hierarchy.
+ * @hide
+ */
+public abstract class TokenBindingService {
+
+    public static final String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5";
+    public static final String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS";
+    public static final String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
+
+    /**
+     * Returns the default TokenBinding service instance. At present there is
+     * only one token binding service instance for all WebView instances,
+     * however this restriction may be relaxed in the future.
+     *
+     * @return The default TokenBindingService instance.
+     */
+    public static TokenBindingService getInstance() {
+        return WebViewFactory.getProvider().getTokenBindingService();
+    }
+
+    /**
+     * Enables the token binding protocol. The token binding protocol
+     * has to be enabled before creating any WebViews.
+     *
+     * @throws IllegalStateException if a WebView was already created.
+     */
+    public abstract void enableTokenBinding();
+
+    /**
+     * Retrieves the key pair for a given origin from the internal
+     * TokenBinding key store asynchronously.
+     * Will create a key pair if one does not exist.
+     *
+     * @param origin The origin for the server.
+     * @param algorithm The algorithm for generating the token binding key.
+     * @param callback The callback that will be called when key is available.
+     *        Cannot be null.
+     */
+    public abstract void getKey(Uri origin,
+                                String algorithm,
+                                ValueCallback<KeyPair> callback);
+    /**
+     * Deletes specified key (for use when associated cookie is cleared).
+     *
+     * @param origin The origin of the server.
+     * @param callback The callback that will be called when key is deleted. The
+     *        callback parameter (Boolean) will indicate if operation is
+     *        successful or if failed. The callback can be null.
+     */
+    public abstract void deleteKey(Uri origin,
+                                   ValueCallback<Boolean> callback);
+
+     /**
+      * Deletes all the keys (for use when cookies are cleared).
+      *
+      * @param callback The callback that will be called when keys are deleted.
+      *        The callback parameter (Boolean) will indicate if operation is
+      *        successful or if failed. The callback can be null.
+      */
+    public abstract void deleteAllKeys(ValueCallback<Boolean> callback);
+}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 9442226..9595db2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -987,9 +987,6 @@
      * @deprecated Database paths are managed by the implementation and calling this method
      *             will have no effect.
      */
-    // This will update WebCore when the Sync runs in the C++ side.
-    // Note that the WebCore Database Tracker only allows the path to be set
-    // once.
     @Deprecated
     public abstract void setDatabasePath(String databasePath);
 
@@ -1000,8 +997,10 @@
      *
      * @param databasePath a path to the directory where databases should be
      *                     saved.
+     * @deprecated Geolocation database are managed by the implementation and calling this method
+     *             will have no effect.
      */
-    // This will update WebCore when the Sync runs in the C++ side.
+    @Deprecated
     public abstract void setGeolocationDatabasePath(String databasePath);
 
     /**
@@ -1102,8 +1101,6 @@
      *   via the JavaScript Geolocation API.
      * </ul>
      * <p>
-     * As an option, it is possible to store previous locations and web origin
-     * permissions in a database. See {@link #setGeolocationDatabasePath}.
      *
      * @param flag whether Geolocation should be enabled
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7443bce..0f58ba3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -280,6 +280,23 @@
  * instead.
  * </p>
  *
+ * <h3>Metrics</h3>
+ *
+ * <p>
+ * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
+ * helps Google improve WebView. Data is collected on a per-app basis for each app which has
+ * instantiated a WebView. An individual app can opt out of this feature by putting the following
+ * tag in its manifest:
+ * </p>
+ * <pre>
+ * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
+ *            android:value="true" />
+ * </pre>
+ * <p>
+ * Data will only be uploaded for a given app if the user has consented AND the app has not opted
+ * out.
+ * </p>
+ *
  */
 // Implementation notes.
 // The WebView is a thin API class that delegates its public API to a backend WebViewProvider
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 6aa5e2f..054eafc 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -131,6 +131,8 @@
     private static String TAG_WEBVIEW_PROVIDER = "webviewprovider";
     private static String TAG_PACKAGE_NAME = "packageName";
     private static String TAG_DESCRIPTION = "description";
+    // Whether or not the provider must be explicitly chosen by the user to be used.
+    private static String TAG_AVAILABILITY = "availableByDefault";
     private static String TAG_SIGNATURE = "signature";
 
     /**
@@ -180,8 +182,12 @@
                         throw new MissingWebViewPackageException(
                                 "WebView provider in framework resources missing description");
                     }
+                    String availableByDefault = parser.getAttributeValue(null, TAG_AVAILABILITY);
+                    if (availableByDefault == null) {
+                        availableByDefault = "false";
+                    }
                     webViewProviders.add(
-                            new WebViewProviderInfo(packageName, description,
+                            new WebViewProviderInfo(packageName, description, availableByDefault,
                                 readSignatures(parser)));
                 }
                 else {
@@ -271,18 +277,25 @@
 
     private static Class<WebViewFactoryProvider> getProviderClass() {
         try {
-            // First fetch the package info so we can log the webview package version.
-            int res = waitForProviderAndSetPackageInfo();
-            if (res != LIBLOAD_SUCCESS) {
-                throw new MissingWebViewPackageException(
-                        "Failed to load WebView provider, error: "
-                        + getWebViewPreparationErrorReason(res));
+            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
+                    "WebViewFactory.waitForProviderAndSetPackageInfo()");
+            try {
+                // First fetch the package info so we can log the webview package version.
+                int res = waitForProviderAndSetPackageInfo();
+                if (res != LIBLOAD_SUCCESS) {
+                    throw new MissingWebViewPackageException(
+                            "Failed to load WebView provider, error: "
+                            + getWebViewPreparationErrorReason(res));
+                }
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
                 sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
 
             Application initialApplication = AppGlobals.getInitialApplication();
             Context webViewContext = null;
+            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "initialApplication.createPackageContext()");
             try {
                 // Construct a package context to load the Java code into the current app.
                 // This is done as early as possible since by constructing a package context we
@@ -293,6 +306,8 @@
                         Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
             } catch (PackageManager.NameNotFoundException e) {
                 throw new MissingWebViewPackageException(e);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
 
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 9105394..02c911f 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -103,6 +103,15 @@
     CookieManager getCookieManager();
 
     /**
+     * Gets the TokenBindingService instance for this WebView implementation. The
+     * implementation must return the same instance on subsequent calls.
+     *
+     * @return the TokenBindingService instance
+     * @hide
+     */
+    TokenBindingService getTokenBindingService();
+
+    /**
      * Gets the singleton WebIconDatabase instance for this WebView implementation. The
      * implementation must return the same instance on subsequent calls.
      *
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 7bad652..3f50fe2 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -40,9 +40,11 @@
         public WebViewPackageNotFoundException(Exception e) { super(e); }
     }
 
-    public WebViewProviderInfo(String packageName, String description, String[] signatures) {
+    public WebViewProviderInfo(String packageName, String description, String availableByDefault,
+            String[] signatures) {
         this.packageName = packageName;
         this.description = description;
+        this.availableByDefault = availableByDefault.equals("true");
         this.signatures = signatures;
     }
 
@@ -89,6 +91,39 @@
         return false;
     }
 
+    /**
+     * Returns whether this package is enabled.
+     * This state can be changed by the user from Settings->Apps
+     */
+    public boolean isEnabled() {
+        try {
+            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+            int enabled_state = pm.getApplicationEnabledSetting(packageName);
+            switch (enabled_state) {
+                case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                    return true;
+                case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+                    ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
+                    return applicationInfo.enabled;
+                default:
+                    return false;
+            }
+        } catch (WebViewPackageNotFoundException e) {
+            return false;
+        } catch (IllegalArgumentException e) {
+            // Thrown by PackageManager.getApplicationEnabledSetting if the package does not exist
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether the provider is always available as long as it is valid.
+     * If this returns false, the provider will only be used if the user chose this provider.
+     */
+    public boolean isAvailableByDefault() {
+        return availableByDefault;
+    }
+
     public PackageInfo getPackageInfo() {
         if (packageInfo == null) {
             try {
@@ -135,13 +170,13 @@
     // fields read from framework resource
     public String packageName;
     public String description;
+    private boolean availableByDefault;
 
     private String[] signatures;
 
     private PackageInfo packageInfo;
-    // flags declaring we want extra info from the package manager
-    private final static int PACKAGE_FLAGS =
-        PackageManager.GET_META_DATA
-        | PackageManager.GET_SIGNATURES;
-}
 
+    // flags declaring we want extra info from the package manager
+    private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
+            | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index cf4587d..4ca8971 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -44,6 +44,12 @@
     private boolean mHasThumbTint = false;
     private boolean mHasThumbTintMode = false;
 
+    private Drawable mTickMark;
+    private ColorStateList mTickMarkTintList = null;
+    private PorterDuff.Mode mTickMarkTintMode = null;
+    private boolean mHasTickMarkTint = false;
+    private boolean mHasTickMarkTintMode = false;
+
     private int mThumbOffset;
     private boolean mSplitTrack;
 
@@ -103,10 +109,25 @@
             mHasThumbTint = true;
         }
 
+        final Drawable tickMark = a.getDrawable(R.styleable.SeekBar_tickMark);
+        setTickMark(tickMark);
+
+        if (a.hasValue(R.styleable.SeekBar_tickMarkTintMode)) {
+            mTickMarkTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.SeekBar_tickMarkTintMode, -1), mTickMarkTintMode);
+            mHasTickMarkTintMode = true;
+        }
+
+        if (a.hasValue(R.styleable.SeekBar_tickMarkTint)) {
+            mTickMarkTintList = a.getColorStateList(R.styleable.SeekBar_tickMarkTint);
+            mHasTickMarkTint = true;
+        }
+
         mSplitTrack = a.getBoolean(R.styleable.SeekBar_splitTrack, false);
 
         // Guess thumb offset if thumb != null, but allow layout to override.
-        final int thumbOffset = a.getDimensionPixelOffset(R.styleable.SeekBar_thumbOffset, getThumbOffset());
+        final int thumbOffset = a.getDimensionPixelOffset(
+                R.styleable.SeekBar_thumbOffset, getThumbOffset());
         setThumbOffset(thumbOffset);
 
         final boolean useDisabledAlpha = a.getBoolean(R.styleable.SeekBar_useDisabledAlpha, true);
@@ -121,6 +142,7 @@
         }
 
         applyThumbTint();
+        applyTickMarkTint();
 
         mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
@@ -313,6 +335,123 @@
     }
 
     /**
+     * Sets the drawable displayed at each progress position, e.g. at each
+     * possible thumb position.
+     *
+     * @param tickMark the drawable to display at each progress position
+     */
+    public void setTickMark(Drawable tickMark) {
+        if (mTickMark != null) {
+            mTickMark.setCallback(null);
+        }
+
+        mTickMark = tickMark;
+
+        if (tickMark != null) {
+            tickMark.setCallback(this);
+            tickMark.setLayoutDirection(getLayoutDirection());
+            if (tickMark.isStateful()) {
+                tickMark.setState(getDrawableState());
+            }
+            applyTickMarkTint();
+        }
+
+        invalidate();
+    }
+
+    /**
+     * @return the drawable displayed at each progress position
+     */
+    public Drawable getTickMark() {
+        return mTickMark;
+    }
+
+    /**
+     * Applies a tint to the tick mark drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link #setTickMark(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTintList(ColorStateList)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#SeekBar_tickMarkTint
+     * @see #getTickMarkTintList()
+     * @see Drawable#setTintList(ColorStateList)
+     */
+    public void setTickMarkTintList(@Nullable ColorStateList tint) {
+        mTickMarkTintList = tint;
+        mHasTickMarkTint = true;
+
+        applyTickMarkTint();
+    }
+
+    /**
+     * Returns the tint applied to the tick mark drawable, if specified.
+     *
+     * @return the tint applied to the tick mark drawable
+     * @attr ref android.R.styleable#SeekBar_tickMarkTint
+     * @see #setTickMarkTintList(ColorStateList)
+     */
+    @Nullable
+    public ColorStateList getTickMarkTintList() {
+        return mTickMarkTintList;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setTickMarkTintList(ColorStateList)}} to the tick mark drawable. The
+     * default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#SeekBar_tickMarkTintMode
+     * @see #getTickMarkTintMode()
+     * @see Drawable#setTintMode(PorterDuff.Mode)
+     */
+    public void setTickMarkTintMode(@Nullable PorterDuff.Mode tintMode) {
+        mTickMarkTintMode = tintMode;
+        mHasTickMarkTintMode = true;
+
+        applyTickMarkTint();
+    }
+
+    /**
+     * Returns the blending mode used to apply the tint to the tick mark drawable,
+     * if specified.
+     *
+     * @return the blending mode used to apply the tint to the tick mark drawable
+     * @attr ref android.R.styleable#SeekBar_tickMarkTintMode
+     * @see #setTickMarkTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getTickMarkTintMode() {
+        return mTickMarkTintMode;
+    }
+
+    private void applyTickMarkTint() {
+        if (mTickMark != null && (mHasTickMarkTint || mHasTickMarkTintMode)) {
+            mTickMark = mTickMark.mutate();
+
+            if (mHasTickMarkTint) {
+                mTickMark.setTintList(mTickMarkTintList);
+            }
+
+            if (mHasTickMarkTintMode) {
+                mTickMark.setTintMode(mTickMarkTintMode);
+            }
+
+            // The drawable (or one of its children) may not have been
+            // stateful before applying the tint, so let's try again.
+            if (mTickMark.isStateful()) {
+                mTickMark.setState(getDrawableState());
+            }
+        }
+    }
+
+    /**
      * Sets the amount of progress changed via the arrow keys.
      *
      * @param increment The amount to increment or decrement when the user
@@ -347,7 +486,7 @@
 
     @Override
     protected boolean verifyDrawable(Drawable who) {
-        return who == mThumb || super.verifyDrawable(who);
+        return who == mThumb || who == mTickMark || super.verifyDrawable(who);
     }
 
     @Override
@@ -357,6 +496,10 @@
         if (mThumb != null) {
             mThumb.jumpToCurrentState();
         }
+
+        if (mTickMark != null) {
+            mTickMark.jumpToCurrentState();
+        }
     }
 
     @Override
@@ -373,6 +516,12 @@
                 && thumb.setState(getDrawableState())) {
             invalidateDrawable(thumb);
         }
+
+        final Drawable tickMark = mTickMark;
+        if (tickMark != null && tickMark.isStateful()
+                && tickMark.setState(getDrawableState())) {
+            invalidateDrawable(tickMark);
+        }
     }
 
     @Override
@@ -524,9 +673,36 @@
             final int saveCount = canvas.save();
             canvas.clipRect(tempRect, Op.DIFFERENCE);
             super.drawTrack(canvas);
+            drawTickMarks(canvas);
             canvas.restoreToCount(saveCount);
         } else {
             super.drawTrack(canvas);
+            drawTickMarks(canvas);
+        }
+    }
+
+    /**
+     * Draw the tick marks.
+     */
+    void drawTickMarks(Canvas canvas) {
+        if (mTickMark != null) {
+            final int count = getMax();
+            if (count > 1) {
+                final int w = mTickMark.getIntrinsicWidth();
+                final int h = mTickMark.getIntrinsicHeight();
+                final int halfW = w >= 0 ? w / 2 : 1;
+                final int halfH = h >= 0 ? h / 2 : 1;
+                mTickMark.setBounds(-halfW, -halfH, halfW, halfH);
+
+                final int spacing = (getWidth() - mPaddingLeft - mPaddingRight) / count;
+                final int saveCount = canvas.save();
+                canvas.translate(mPaddingLeft, getHeight() / 2);
+                for (int i = 0; i <= count; i++) {
+                    mTickMark.draw(canvas);
+                    canvas.translate(spacing, 0);
+                }
+                canvas.restoreToCount(saveCount);
+            }
         }
     }
 
@@ -535,12 +711,12 @@
      */
     void drawThumb(Canvas canvas) {
         if (mThumb != null) {
-            canvas.save();
+            final int saveCount = canvas.save();
             // Translate the padding. For the x, we need to allow the thumb to
             // draw in its extra space
             canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop);
             mThumb.draw(canvas);
-            canvas.restore();
+            canvas.restoreToCount(saveCount);
         }
     }
 
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 0032f17..ac8d578 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -20,6 +20,8 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -57,7 +59,6 @@
  */
 public class ActionMenuPresenter extends BaseMenuPresenter
         implements ActionProvider.SubUiVisibilityListener {
-    private static final String TAG = "ActionMenuPresenter";
     private static final int ITEM_ANIMATION_DURATION = 150;
     private static final boolean ACTIONBAR_ANIMATIONS_ENABLED = false;
 
@@ -85,20 +86,16 @@
     private OpenOverflowRunnable mPostedOpenRunnable;
     private ActionMenuPopupCallback mPopupCallback;
 
-    private final boolean mShowCascadingMenus;
-
     final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
     int mOpenSubMenuId;
 
     // These collections are used to store pre- and post-layout information for menu items,
     // which is used to determine appropriate animations to run for changed items.
-    private SparseArray<MenuItemLayoutInfo> mPreLayoutItems =
-            new SparseArray<MenuItemLayoutInfo>();
-    private SparseArray<MenuItemLayoutInfo> mPostLayoutItems =
-            new SparseArray<MenuItemLayoutInfo>();
+    private SparseArray<MenuItemLayoutInfo> mPreLayoutItems = new SparseArray<>();
+    private SparseArray<MenuItemLayoutInfo> mPostLayoutItems = new SparseArray<>();
 
     // The list of currently running animations on menu items.
-    private List<ItemAnimationInfo> mRunningItemAnimations = new ArrayList<ItemAnimationInfo>();
+    private List<ItemAnimationInfo> mRunningItemAnimations = new ArrayList<>();
     private ViewTreeObserver.OnPreDrawListener mItemAnimationPreDrawListener =
             new ViewTreeObserver.OnPreDrawListener() {
         @Override
@@ -128,13 +125,10 @@
     public ActionMenuPresenter(Context context) {
         super(context, com.android.internal.R.layout.action_menu_layout,
                 com.android.internal.R.layout.action_menu_item_layout);
-
-        mShowCascadingMenus = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_enableCascadingSubmenus);
     }
 
     @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
+    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
         super.initForMenu(context, menu);
 
         final Resources res = context.getResources();
@@ -629,8 +623,16 @@
     }
 
     public boolean flagActionItems() {
-        final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
-        final int itemsSize = visibleItems.size();
+        final ArrayList<MenuItemImpl> visibleItems;
+        final int itemsSize;
+        if (mMenu != null) {
+            visibleItems = mMenu.getVisibleItems();
+            itemsSize = visibleItems.size();
+        } else {
+            visibleItems = null;
+            itemsSize = 0;
+        }
+
         int maxActions = mMaxItems;
         int widthLimit = mActionItemWidthLimit;
         final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
@@ -786,7 +788,7 @@
         if (isVisible) {
             // Not a submenu, but treat it like one.
             super.onSubMenuSelected(null);
-        } else {
+        } else if (mMenu != null) {
             mMenu.close(false /* closeAllMenus */);
         }
     }
@@ -835,8 +837,6 @@
     }
 
     private class OverflowMenuButton extends ImageButton implements ActionMenuView.ActionMenuChildView {
-        private final float[] mTempPts = new float[2];
-
         public OverflowMenuButton(Context context) {
             super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
 
@@ -938,7 +938,9 @@
 
         @Override
         protected void onDismiss() {
-            mMenu.close();
+            if (mMenu != null) {
+                mMenu.close();
+            }
             mOverflowPopup = null;
 
             super.onDismiss();
@@ -999,7 +1001,9 @@
         }
 
         public void run() {
-            mMenu.changeMenuMode();
+            if (mMenu != null) {
+                mMenu.changeMenuMode();
+            }
             final View menuView = (View) mMenuView;
             if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
                 mOverflowPopup = mPopup;
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index 1f02c3b..4d0a1c8 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -622,7 +622,7 @@
     }
 
     /** @hide */
-    public void initialize(MenuBuilder menu) {
+    public void initialize(@Nullable MenuBuilder menu) {
         mMenu = menu;
     }
 
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 2aaa356..cde7604 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -16,8 +16,11 @@
 
 package android.widget;
 
+import android.annotation.AttrRes;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StyleRes;
 import android.annotation.Widget;
 import android.content.Context;
@@ -37,9 +40,13 @@
 import java.util.TimeZone;
 
 /**
- * This class is a calendar widget for displaying and selecting dates. The range
- * of dates supported by this calendar is configurable. A user can select a date
- * by taping on it and can scroll and fling the calendar to a desired date.
+ * This class is a calendar widget for displaying and selecting dates. The
+ * range of dates supported by this calendar is configurable.
+ * <p>
+ * The exact appearance and interaction model of this widget may vary between
+ * OS versions and themes (e.g. Holo versus Material), but in general a user
+ * can select a date by tapping on it and can scroll or fling the calendar to a
+ * desired date.
  *
  * @attr ref android.R.styleable#CalendarView_showWeekNumber
  * @attr ref android.R.styleable#CalendarView_firstDayOfWeek
@@ -77,22 +84,24 @@
          * @param month The month that was set [0-11].
          * @param dayOfMonth The day of the month that was set.
          */
-        public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth);
+        void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth);
     }
 
-    public CalendarView(Context context) {
+    public CalendarView(@NonNull Context context) {
         this(context, null);
     }
 
-    public CalendarView(Context context, AttributeSet attrs) {
+    public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.calendarViewStyle);
     }
 
-    public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public CalendarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
         final TypedArray a = context.obtainStyledAttributes(
@@ -322,7 +331,7 @@
      *
      * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
      */
-    public void setWeekDayTextAppearance(int resourceId) {
+    public void setWeekDayTextAppearance(@StyleRes int resourceId) {
         mDelegate.setWeekDayTextAppearance(resourceId);
     }
 
@@ -333,7 +342,7 @@
      *
      * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
      */
-    public int getWeekDayTextAppearance() {
+    public @StyleRes int getWeekDayTextAppearance() {
         return mDelegate.getWeekDayTextAppearance();
     }
 
@@ -344,7 +353,7 @@
      *
      * @attr ref android.R.styleable#CalendarView_dateTextAppearance
      */
-    public void setDateTextAppearance(int resourceId) {
+    public void setDateTextAppearance(@StyleRes int resourceId) {
         mDelegate.setDateTextAppearance(resourceId);
     }
 
@@ -355,7 +364,7 @@
      *
      * @attr ref android.R.styleable#CalendarView_dateTextAppearance
      */
-    public int getDateTextAppearance() {
+    public @StyleRes int getDateTextAppearance() {
         return mDelegate.getDateTextAppearance();
     }
 
@@ -552,36 +561,29 @@
         int getShownWeekCount();
 
         void setSelectedWeekBackgroundColor(@ColorInt int color);
-        @ColorInt
-        int getSelectedWeekBackgroundColor();
+        @ColorInt int getSelectedWeekBackgroundColor();
 
         void setFocusedMonthDateColor(@ColorInt int color);
-        @ColorInt
-        int getFocusedMonthDateColor();
+        @ColorInt int getFocusedMonthDateColor();
 
         void setUnfocusedMonthDateColor(@ColorInt int color);
-        @ColorInt
-        int getUnfocusedMonthDateColor();
+        @ColorInt int getUnfocusedMonthDateColor();
 
         void setWeekNumberColor(@ColorInt int color);
-        @ColorInt
-        int getWeekNumberColor();
+        @ColorInt int getWeekNumberColor();
 
         void setWeekSeparatorLineColor(@ColorInt int color);
-        @ColorInt
-        int getWeekSeparatorLineColor();
+        @ColorInt int getWeekSeparatorLineColor();
 
         void setSelectedDateVerticalBar(@DrawableRes int resourceId);
         void setSelectedDateVerticalBar(Drawable drawable);
         Drawable getSelectedDateVerticalBar();
 
         void setWeekDayTextAppearance(@StyleRes int resourceId);
-        @StyleRes
-        int getWeekDayTextAppearance();
+        @StyleRes int getWeekDayTextAppearance();
 
         void setDateTextAppearance(@StyleRes int resourceId);
-        @StyleRes
-        int getDateTextAppearance();
+        @StyleRes int getDateTextAppearance();
 
         void setMinDate(long minDate);
         long getMinDate();
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 5f5943f..87bee44 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -999,8 +999,8 @@
 
         private boolean isNewDate(int year, int month, int dayOfMonth) {
             return (mCurrentDate.get(Calendar.YEAR) != year
-                    || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
-                    || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month);
+                    || mCurrentDate.get(Calendar.MONTH) != month
+                    || mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth);
         }
 
         private void setDate(int year, int month, int dayOfMonth) {
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
index 2fb2101..02f7e7a 100644
--- a/core/java/android/widget/DropDownListView.java
+++ b/core/java/android/widget/DropDownListView.java
@@ -19,18 +19,10 @@
 
 import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.IntProperty;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.TextView;
-import android.widget.ListView;
-
 
 /**
  * Wrapper class for a ListView. This wrapper can hijack the focus to
@@ -41,26 +33,6 @@
  * @hide
  */
 public class DropDownListView extends ListView {
-    /** Duration in milliseconds of the drag-to-open click animation. */
-    private static final long CLICK_ANIM_DURATION = 150;
-
-    /** Target alpha value for drag-to-open click animation. */
-    private static final int CLICK_ANIM_ALPHA = 0x80;
-
-    /** Wrapper around Drawable's <code>alpha</code> property. */
-    private static final IntProperty<Drawable> DRAWABLE_ALPHA =
-            new IntProperty<Drawable>("alpha") {
-                @Override
-                public void setValue(Drawable object, int value) {
-                    object.setAlpha(value);
-                }
-
-                @Override
-                public Integer get(Drawable object) {
-                    return object.getAlpha();
-                }
-            };
-
     /*
      * WARNING: This is a workaround for a touch mode issue.
      *
@@ -99,9 +71,6 @@
     /** Whether to force drawing of the pressed state selector. */
     private boolean mDrawsInPressedState;
 
-    /** Current drag-to-open click animation, if any. */
-    private Animator mClickAnimation;
-
     /** Helper for drag-to-open auto scrolling. */
     private AbsListViewAutoScroller mScrollHelper;
 
@@ -110,7 +79,7 @@
      *
      * @param context this view's context
      */
-    public DropDownListView(Context context, boolean hijackFocus) {
+    public DropDownListView(@NonNull Context context, boolean hijackFocus) {
         this(context, hijackFocus, com.android.internal.R.attr.dropDownListViewStyle);
     }
 
@@ -119,7 +88,7 @@
      *
      * @param context this view's context
      */
-    public DropDownListView(Context context, boolean hijackFocus, int defStyleAttr) {
+    public DropDownListView(@NonNull Context context, boolean hijackFocus, int defStyleAttr) {
         super(context, null, defStyleAttr);
         mHijackFocus = hijackFocus;
         // TODO: Add an API to control this
@@ -132,7 +101,7 @@
     }
 
     @Override
-    public boolean onHoverEvent(MotionEvent ev) {
+    public boolean onHoverEvent(@NonNull MotionEvent ev) {
         // Allow the super class to handle hover state management first.
         final boolean handled = super.onHoverEvent(ev);
 
@@ -169,7 +138,7 @@
      * @param activePointerId id of the pointer that activated forwarding
      * @return whether the event was handled
      */
-    public boolean onForwardedEvent(MotionEvent event, int activePointerId) {
+    public boolean onForwardedEvent(@NonNull MotionEvent event, int activePointerId) {
         boolean handledEvent = true;
         boolean clearPressedItem = false;
 
@@ -201,7 +170,8 @@
                 handledEvent = true;
 
                 if (actionMasked == MotionEvent.ACTION_UP) {
-                    clickPressedItem(child, position);
+                    final long id = getItemIdAtPosition(position);
+                    performItemClick(child, position, id);
                 }
                 break;
         }
@@ -234,30 +204,6 @@
         this.mListSelectionHidden = listSelectionHidden;
     }
 
-    /**
-     * Starts an alpha animation on the selector. When the animation ends,
-     * the list performs a click on the item.
-     */
-    private void clickPressedItem(final View child, final int position) {
-        final long id = getItemIdAtPosition(position);
-        final Animator anim = ObjectAnimator.ofInt(
-                mSelector, DRAWABLE_ALPHA, 0xFF, CLICK_ANIM_ALPHA, 0xFF);
-        anim.setDuration(CLICK_ANIM_DURATION);
-        anim.setInterpolator(new AccelerateDecelerateInterpolator());
-        anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-            public void onAnimationEnd(Animator animation) {
-                performItemClick(child, position, id);
-            }
-        });
-        anim.start();
-
-        if (mClickAnimation != null) {
-            mClickAnimation.cancel();
-        }
-        mClickAnimation = anim;
-    }
-
     private void clearPressedItem() {
         mDrawsInPressedState = false;
         setPressed(false);
@@ -267,14 +213,9 @@
         if (motionView != null) {
             motionView.setPressed(false);
         }
-
-        if (mClickAnimation != null) {
-            mClickAnimation.cancel();
-            mClickAnimation = null;
-        }
     }
 
-    private void setPressedItem(View child, int position, float x, float y) {
+    private void setPressedItem(@NonNull View child, int position, float x, float y) {
         mDrawsInPressedState = true;
 
         // Ordering is essential. First, update the container's pressed state.
@@ -311,11 +252,6 @@
         // Refresh the drawable state to reflect the new pressed state,
         // which will also update the selector state.
         refreshDrawableState();
-
-        if (mClickAnimation != null) {
-            mClickAnimation.cancel();
-            mClickAnimation = null;
-        }
     }
 
     @Override
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 15cea77..1576877 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -82,6 +82,7 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.RenderNode;
+import android.view.SubMenu;
 import android.view.View;
 import android.view.View.DragShadowBuilder;
 import android.view.View.OnClickListener;
@@ -285,6 +286,9 @@
 
     boolean mIsInsertionActionModeStartPending = false;
 
+    private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
+    private SuggestionInfo[] mSuggestionInfosInContextMenu;
+
     Editor(TextView textView) {
         mTextView = textView;
         // Synchronize the filter list, which places the undo input filter at the end.
@@ -2369,6 +2373,9 @@
         if (offset == -1) {
             return;
         }
+        mPreserveDetachedSelection = true;
+        stopTextActionMode();
+        mPreserveDetachedSelection = false;
         final boolean isOnSelection = mTextView.hasSelection()
                 && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
         if (!isOnSelection) {
@@ -2378,7 +2385,24 @@
             Selection.setSelection((Spannable) mTextView.getText(), offset);
         }
 
-        // TODO: Add suggestions in the context menu.
+        if (shouldOfferToShowSuggestions()) {
+            if (mSuggestionInfosInContextMenu == null) {
+                mSuggestionInfosInContextMenu =
+                        new SuggestionInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE];
+                for (int i = 0; i < mSuggestionInfosInContextMenu.length; i++) {
+                    mSuggestionInfosInContextMenu[i] = new SuggestionInfo();
+                }
+            }
+            final SubMenu subMenu = menu.addSubMenu(Menu.NONE, Menu.NONE, MENU_ITEM_ORDER_REPLACE,
+                    com.android.internal.R.string.replace);
+            mSuggestionHelper.getSuggestionInfo(mSuggestionInfosInContextMenu);
+            int i = 0;
+            for (final SuggestionInfo info : mSuggestionInfosInContextMenu) {
+                info.mSuggestionEnd = info.mText.length();
+                subMenu.add(Menu.NONE, Menu.NONE, i++, info.mText)
+                        .setOnMenuItemClickListener(mOnContextMenuReplaceItemClickListener);
+            }
+        }
 
         menu.add(Menu.NONE, TextView.ID_UNDO, MENU_ITEM_ORDER_UNDO,
                 com.android.internal.R.string.undo)
@@ -2422,6 +2446,61 @@
         mPreserveDetachedSelection = true;
     }
 
+    private void replaceWithSuggestion(SuggestionInfo suggestionInfo, int spanStart, int spanEnd) {
+        final Editable editable = (Editable) mTextView.getText();
+        final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
+        // SuggestionSpans are removed by replace: save them before
+        SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
+                SuggestionSpan.class);
+        final int length = suggestionSpans.length;
+        int[] suggestionSpansStarts = new int[length];
+        int[] suggestionSpansEnds = new int[length];
+        int[] suggestionSpansFlags = new int[length];
+        for (int i = 0; i < length; i++) {
+            final SuggestionSpan suggestionSpan = suggestionSpans[i];
+            suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
+            suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
+            suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
+
+            // Remove potential misspelled flags
+            int suggestionSpanFlags = suggestionSpan.getFlags();
+            if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+                suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
+                suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
+                suggestionSpan.setFlags(suggestionSpanFlags);
+            }
+        }
+
+        // Notify source IME of the suggestion pick. Do this before swapping texts.
+        suggestionInfo.mSuggestionSpan.notifySelection(
+                mTextView.getContext(), originalText, suggestionInfo.mSuggestionIndex);
+
+        // Swap text content between actual text and Suggestion span
+        final int suggestionStart = suggestionInfo.mSuggestionStart;
+        final int suggestionEnd = suggestionInfo.mSuggestionEnd;
+        final String suggestion = suggestionInfo.mText.subSequence(
+                suggestionStart, suggestionEnd).toString();
+        mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
+
+        String[] suggestions = suggestionInfo.mSuggestionSpan.getSuggestions();
+        suggestions[suggestionInfo.mSuggestionIndex] = originalText;
+
+        // Restore previous SuggestionSpans
+        final int lengthDelta = suggestion.length() - (spanEnd - spanStart);
+        for (int i = 0; i < length; i++) {
+            // Only spans that include the modified region make sense after replacement
+            // Spans partially included in the replaced region are removed, there is no
+            // way to assign them a valid range after replacement
+            if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) {
+                mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
+                        suggestionSpansEnds[i] + lengthDelta, suggestionSpansFlags[i]);
+            }
+        }
+        // Move cursor at the end of the replaced word
+        final int newCursorPosition = spanEnd + lengthDelta;
+        mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
+    }
+
     private final MenuItem.OnMenuItemClickListener mOnContextMenuItemClickListener =
             new MenuItem.OnMenuItemClickListener() {
         @Override
@@ -2433,6 +2512,31 @@
         }
     };
 
+    private final MenuItem.OnMenuItemClickListener mOnContextMenuReplaceItemClickListener =
+            new MenuItem.OnMenuItemClickListener() {
+        @Override
+        public boolean onMenuItemClick(MenuItem item) {
+            int index = item.getOrder();
+            if (index < 0 || index >= mSuggestionInfosInContextMenu.length) {
+                clear();
+                return false;
+            }
+            final Spannable spannable = (Spannable) mTextView.getText();
+            final SuggestionSpan suggestionSpan =
+                    mSuggestionInfosInContextMenu[index].mSuggestionSpan;
+            replaceWithSuggestion(mSuggestionInfosInContextMenu[index],
+                    spannable.getSpanStart(suggestionSpan), spannable.getSpanEnd(suggestionSpan));
+            clear();
+            return true;
+        }
+
+        private void clear() {
+            for (final SuggestionInfo info : mSuggestionInfosInContextMenu) {
+                info.clear();
+            }
+        }
+    };
+
     /**
      * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
      * pop-up should be displayed.
@@ -2849,6 +2953,131 @@
         }
     }
 
+    private static class SuggestionInfo {
+        // Range of actual suggestion within text
+        int mSuggestionStart, mSuggestionEnd;
+
+        // The SuggestionSpan that this TextView represents
+        @Nullable
+        SuggestionSpan mSuggestionSpan;
+
+        // The index of this suggestion inside suggestionSpan
+        int mSuggestionIndex;
+
+        final SpannableStringBuilder mText = new SpannableStringBuilder();
+
+        void clear() {
+            mSuggestionSpan = null;
+            mText.clear();
+        }
+    }
+
+    private class SuggestionHelper {
+        private final Comparator<SuggestionSpan> mSuggestionSpanComparator =
+                new SuggestionSpanComparator();
+        private final HashMap<SuggestionSpan, Integer> mSpansLengths =
+                new HashMap<SuggestionSpan, Integer>();
+
+        private class SuggestionSpanComparator implements Comparator<SuggestionSpan> {
+            public int compare(SuggestionSpan span1, SuggestionSpan span2) {
+                final int flag1 = span1.getFlags();
+                final int flag2 = span2.getFlags();
+                if (flag1 != flag2) {
+                    // The order here should match what is used in updateDrawState
+                    final boolean easy1 = (flag1 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
+                    final boolean easy2 = (flag2 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
+                    final boolean misspelled1 = (flag1 & SuggestionSpan.FLAG_MISSPELLED) != 0;
+                    final boolean misspelled2 = (flag2 & SuggestionSpan.FLAG_MISSPELLED) != 0;
+                    if (easy1 && !misspelled1) return -1;
+                    if (easy2 && !misspelled2) return 1;
+                    if (misspelled1) return -1;
+                    if (misspelled2) return 1;
+                }
+
+                return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue();
+            }
+        }
+
+        /**
+         * Returns the suggestion spans that cover the current cursor position. The suggestion
+         * spans are sorted according to the length of text that they are attached to.
+         */
+        private SuggestionSpan[] getSortedSuggestionSpans() {
+            int pos = mTextView.getSelectionStart();
+            Spannable spannable = (Spannable) mTextView.getText();
+            SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
+
+            mSpansLengths.clear();
+            for (SuggestionSpan suggestionSpan : suggestionSpans) {
+                int start = spannable.getSpanStart(suggestionSpan);
+                int end = spannable.getSpanEnd(suggestionSpan);
+                mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start));
+            }
+
+            // The suggestions are sorted according to their types (easy correction first, then
+            // misspelled) and to the length of the text that they cover (shorter first).
+            Arrays.sort(suggestionSpans, mSuggestionSpanComparator);
+            mSpansLengths.clear();
+
+            return suggestionSpans;
+        }
+
+        /**
+         * Gets the SuggestionInfo list that contains suggestion information at the current cursor
+         * position.
+         *
+         * @param suggestionInfos SuggestionInfo array the results will be set.
+         * @return the number of suggestions actually fetched.
+         */
+        public int getSuggestionInfo(SuggestionInfo[] suggestionInfos) {
+            final Spannable spannable = (Spannable) mTextView.getText();
+            final SuggestionSpan[] suggestionSpans = getSortedSuggestionSpans();
+            final int nbSpans = suggestionSpans.length;
+            if (nbSpans == 0) return 0;
+
+            int numberOfSuggestions = 0;
+            for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
+                final SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
+                final int spanStart = spannable.getSpanStart(suggestionSpan);
+                final int spanEnd = spannable.getSpanEnd(suggestionSpan);
+
+                final String[] suggestions = suggestionSpan.getSuggestions();
+                final int nbSuggestions = suggestions.length;
+                for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
+                    final String suggestion = suggestions[suggestionIndex];
+                    boolean suggestionIsDuplicate = false;
+                    for (int i = 0; i < numberOfSuggestions; i++) {
+                        if (suggestionInfos[i].mText.toString().equals(suggestion)) {
+                            final SuggestionSpan otherSuggestionSpan =
+                                    suggestionInfos[i].mSuggestionSpan;
+                            final int otherSpanStart = spannable.getSpanStart(otherSuggestionSpan);
+                            final int otherSpanEnd = spannable.getSpanEnd(otherSuggestionSpan);
+                            if (spanStart == otherSpanStart && spanEnd == otherSpanEnd) {
+                                suggestionIsDuplicate = true;
+                                break;
+                            }
+                        }
+                    }
+
+                    if (suggestionIsDuplicate) {
+                        continue;
+                    }
+                    SuggestionInfo suggestionInfo = suggestionInfos[numberOfSuggestions];
+                    suggestionInfo.mSuggestionSpan = suggestionSpan;
+                    suggestionInfo.mSuggestionIndex = suggestionIndex;
+                    suggestionInfo.mSuggestionStart = 0;
+                    suggestionInfo.mSuggestionEnd = suggestion.length();
+                    suggestionInfo.mText.replace(0, suggestionInfo.mText.length(), suggestion);
+                    numberOfSuggestions++;
+                    if (numberOfSuggestions >= suggestionInfos.length) {
+                        return numberOfSuggestions;
+                    }
+                }
+            }
+            return numberOfSuggestions;
+        }
+    }
+
     @VisibleForTesting
     public class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
         private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
@@ -2862,8 +3091,6 @@
         private boolean mCursorWasVisibleBeforeSuggestions;
         private boolean mIsShowingUp = false;
         private SuggestionAdapter mSuggestionsAdapter;
-        private final Comparator<SuggestionSpan> mSuggestionSpanComparator;
-        private final HashMap<SuggestionSpan, Integer> mSpansLengths;
         private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan(
                 mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle);
         private TextView mAddToDictionaryButton;
@@ -2895,8 +3122,6 @@
 
         public SuggestionsPopupWindow() {
             mCursorWasVisibleBeforeSuggestions = mCursorVisible;
-            mSuggestionSpanComparator = new SuggestionSpanComparator();
-            mSpansLengths = new HashMap<SuggestionSpan, Integer>();
         }
 
         @Override
@@ -2987,24 +3212,6 @@
             mIsShowingUp = false;
         }
 
-        private final class SuggestionInfo {
-            int suggestionStart, suggestionEnd; // range of actual suggestion within text
-
-            // the SuggestionSpan that this TextView represents
-            @Nullable
-            SuggestionSpan suggestionSpan;
-
-            int suggestionIndex; // the index of this suggestion inside suggestionSpan
-
-            @Nullable
-            final SpannableStringBuilder text = new SpannableStringBuilder();
-
-            void clear() {
-                suggestionSpan = null;
-                text.clear();
-            }
-        }
-
         private class SuggestionAdapter extends BaseAdapter {
             private LayoutInflater mInflater = (LayoutInflater) mTextView.getContext().
                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -3034,55 +3241,11 @@
                 }
 
                 final SuggestionInfo suggestionInfo = mSuggestionInfos[position];
-                textView.setText(suggestionInfo.text);
+                textView.setText(suggestionInfo.mText);
                 return textView;
             }
         }
 
-        private class SuggestionSpanComparator implements Comparator<SuggestionSpan> {
-            public int compare(SuggestionSpan span1, SuggestionSpan span2) {
-                final int flag1 = span1.getFlags();
-                final int flag2 = span2.getFlags();
-                if (flag1 != flag2) {
-                    // The order here should match what is used in updateDrawState
-                    final boolean easy1 = (flag1 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
-                    final boolean easy2 = (flag2 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
-                    final boolean misspelled1 = (flag1 & SuggestionSpan.FLAG_MISSPELLED) != 0;
-                    final boolean misspelled2 = (flag2 & SuggestionSpan.FLAG_MISSPELLED) != 0;
-                    if (easy1 && !misspelled1) return -1;
-                    if (easy2 && !misspelled2) return 1;
-                    if (misspelled1) return -1;
-                    if (misspelled2) return 1;
-                }
-
-                return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue();
-            }
-        }
-
-        /**
-         * Returns the suggestion spans that cover the current cursor position. The suggestion
-         * spans are sorted according to the length of text that they are attached to.
-         */
-        private SuggestionSpan[] getSuggestionSpans() {
-            int pos = mTextView.getSelectionStart();
-            Spannable spannable = (Spannable) mTextView.getText();
-            SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
-
-            mSpansLengths.clear();
-            for (SuggestionSpan suggestionSpan : suggestionSpans) {
-                int start = spannable.getSpanStart(suggestionSpan);
-                int end = spannable.getSpanEnd(suggestionSpan);
-                mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start));
-            }
-
-            // The suggestions are sorted according to their types (easy correction first, then
-            // misspelled) and to the length of the text that they cover (shorter first).
-            Arrays.sort(suggestionSpans, mSuggestionSpanComparator);
-            mSpansLengths.clear();
-
-            return suggestionSpans;
-        }
-
         @VisibleForTesting
         public ViewGroup getContentViewForTesting() {
             return mContentView;
@@ -3166,66 +3329,26 @@
 
         private boolean updateSuggestions() {
             Spannable spannable = (Spannable) mTextView.getText();
-            SuggestionSpan[] suggestionSpans = getSuggestionSpans();
+            mNumberOfSuggestions =
+                    mSuggestionHelper.getSuggestionInfo(mSuggestionInfos);
+            if (mNumberOfSuggestions == 0) {
+                return false;
+            }
 
-            final int nbSpans = suggestionSpans.length;
-            // Suggestions are shown after a delay: the underlying spans may have been removed
-            if (nbSpans == 0) return false;
-
-            mNumberOfSuggestions = 0;
             int spanUnionStart = mTextView.getText().length();
             int spanUnionEnd = 0;
 
             mMisspelledSpan = null;
-            int underlineColor = 0;
-
-            for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
-                SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
-                final int spanStart = spannable.getSpanStart(suggestionSpan);
-                final int spanEnd = spannable.getSpanEnd(suggestionSpan);
-                spanUnionStart = Math.min(spanStart, spanUnionStart);
-                spanUnionEnd = Math.max(spanEnd, spanUnionEnd);
-
+            for (int i = 0; i < mNumberOfSuggestions; i++) {
+                final SuggestionInfo suggestionInfo = mSuggestionInfos[i];
+                final SuggestionSpan suggestionSpan = suggestionInfo.mSuggestionSpan;
                 if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
                     mMisspelledSpan = suggestionSpan;
                 }
-
-                // The first span dictates the background color of the highlighted text
-                if (spanIndex == 0) underlineColor = suggestionSpan.getUnderlineColor();
-
-                String[] suggestions = suggestionSpan.getSuggestions();
-                int nbSuggestions = suggestions.length;
-                for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
-                    String suggestion = suggestions[suggestionIndex];
-
-                    boolean suggestionIsDuplicate = false;
-                    for (int i = 0; i < mNumberOfSuggestions; i++) {
-                        if (mSuggestionInfos[i].text.toString().equals(suggestion)) {
-                            SuggestionSpan otherSuggestionSpan = mSuggestionInfos[i].suggestionSpan;
-                            final int otherSpanStart = spannable.getSpanStart(otherSuggestionSpan);
-                            final int otherSpanEnd = spannable.getSpanEnd(otherSuggestionSpan);
-                            if (spanStart == otherSpanStart && spanEnd == otherSpanEnd) {
-                                suggestionIsDuplicate = true;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (!suggestionIsDuplicate) {
-                        SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
-                        suggestionInfo.suggestionSpan = suggestionSpan;
-                        suggestionInfo.suggestionIndex = suggestionIndex;
-                        suggestionInfo.text.replace(0, suggestionInfo.text.length(), suggestion);
-
-                        mNumberOfSuggestions++;
-
-                        if (mNumberOfSuggestions == MAX_NUMBER_SUGGESTIONS) {
-                            // Also end outer for loop
-                            spanIndex = nbSpans;
-                            break;
-                        }
-                    }
-                }
+                final int spanStart = spannable.getSpanStart(suggestionSpan);
+                final int spanEnd = spannable.getSpanEnd(suggestionSpan);
+                spanUnionStart = Math.min(spanUnionStart, spanStart);
+                spanUnionEnd = Math.max(spanUnionEnd, spanEnd);
             }
 
             for (int i = 0; i < mNumberOfSuggestions; i++) {
@@ -3244,6 +3367,7 @@
             mAddToDictionaryButton.setVisibility(addToDictionaryButtonVisibility);
 
             if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+            final int underlineColor = mSuggestionInfos[0].mSuggestionSpan.getUnderlineColor();
             if (underlineColor == 0) {
                 // Fallback on the default highlight color when the first span does not provide one
                 mSuggestionRangeSpan.setBackgroundColor(mTextView.mHighlightColor);
@@ -3263,21 +3387,21 @@
         private void highlightTextDifferences(SuggestionInfo suggestionInfo, int unionStart,
                 int unionEnd) {
             final Spannable text = (Spannable) mTextView.getText();
-            final int spanStart = text.getSpanStart(suggestionInfo.suggestionSpan);
-            final int spanEnd = text.getSpanEnd(suggestionInfo.suggestionSpan);
+            final int spanStart = text.getSpanStart(suggestionInfo.mSuggestionSpan);
+            final int spanEnd = text.getSpanEnd(suggestionInfo.mSuggestionSpan);
 
             // Adjust the start/end of the suggestion span
-            suggestionInfo.suggestionStart = spanStart - unionStart;
-            suggestionInfo.suggestionEnd = suggestionInfo.suggestionStart
-                    + suggestionInfo.text.length();
+            suggestionInfo.mSuggestionStart = spanStart - unionStart;
+            suggestionInfo.mSuggestionEnd = suggestionInfo.mSuggestionStart
+                    + suggestionInfo.mText.length();
 
-            suggestionInfo.text.setSpan(mHighlightSpan, 0, suggestionInfo.text.length(),
+            suggestionInfo.mText.setSpan(mHighlightSpan, 0, suggestionInfo.mText.length(),
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
             // Add the text before and after the span.
             final String textAsString = text.toString();
-            suggestionInfo.text.insert(0, textAsString.substring(unionStart, spanStart));
-            suggestionInfo.text.append(textAsString.substring(spanEnd, unionEnd));
+            suggestionInfo.mText.insert(0, textAsString.substring(unionStart, spanStart));
+            suggestionInfo.mText.append(textAsString.substring(spanEnd, unionEnd));
         }
 
         @Override
@@ -3285,69 +3409,14 @@
             Editable editable = (Editable) mTextView.getText();
             SuggestionInfo suggestionInfo = mSuggestionInfos[position];
 
-            final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan);
-            final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan);
+            final int spanStart = editable.getSpanStart(suggestionInfo.mSuggestionSpan);
+            final int spanEnd = editable.getSpanEnd(suggestionInfo.mSuggestionSpan);
             if (spanStart < 0 || spanEnd <= spanStart) {
                 // Span has been removed
                 hideWithCleanUp();
                 return;
             }
-
-            final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
-
-            // SuggestionSpans are removed by replace: save them before
-            final SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
-                    SuggestionSpan.class);
-            final int length = suggestionSpans.length;
-            final int[] suggestionSpansStarts = new int[length];
-            final int[] suggestionSpansEnds = new int[length];
-            final int[] suggestionSpansFlags = new int[length];
-            for (int i = 0; i < length; i++) {
-                final SuggestionSpan suggestionSpan = suggestionSpans[i];
-                suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
-                suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
-                suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
-
-                // Remove potential misspelled flags
-                int suggestionSpanFlags = suggestionSpan.getFlags();
-                if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
-                    suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
-                    suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
-                    suggestionSpan.setFlags(suggestionSpanFlags);
-                }
-            }
-
-            final int suggestionStart = suggestionInfo.suggestionStart;
-            final int suggestionEnd = suggestionInfo.suggestionEnd;
-            final String suggestion = suggestionInfo.text.subSequence(
-                    suggestionStart, suggestionEnd).toString();
-            mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
-
-            // Notify source IME of the suggestion pick. Do this before
-            // swaping texts.
-            suggestionInfo.suggestionSpan.notifySelection(
-                    mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
-
-            // Swap text content between actual text and Suggestion span
-            final String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
-            suggestions[suggestionInfo.suggestionIndex] = originalText;
-
-            // Restore previous SuggestionSpans
-            final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
-            for (int i = 0; i < length; i++) {
-                // Only spans that include the modified region make sense after replacement
-                // Spans partially included in the replaced region are removed, there is no
-                // way to assign them a valid range after replacement
-                if (suggestionSpansStarts[i] <= spanStart &&
-                        suggestionSpansEnds[i] >= spanEnd) {
-                    mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
-                            suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]);
-                }
-            }
-
-            // Move cursor at the end of the replaced word
-            final int newCursorPosition = spanEnd + lengthDifference;
-            mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
+            replaceWithSuggestion(suggestionInfo, spanStart, spanEnd);
             hideWithCleanUp();
         }
     }
diff --git a/core/java/android/widget/ForwardingListener.java b/core/java/android/widget/ForwardingListener.java
index 7ddeff9..b383e1c 100644
--- a/core/java/android/widget/ForwardingListener.java
+++ b/core/java/android/widget/ForwardingListener.java
@@ -53,12 +53,6 @@
     /** Whether this listener is currently forwarding touch events. */
     private boolean mForwarding;
 
-    /**
-     * Whether forwarding was initiated by a long-press. If so, we won't
-     * force the window to dismiss when the touch stream ends.
-     */
-    private boolean mWasLongPress;
-
     /** The id of the first pointer down in the current event stream. */
     private int mActivePointerId;
 
@@ -172,7 +166,6 @@
         switch (actionMasked) {
             case MotionEvent.ACTION_DOWN:
                 mActivePointerId = srcEvent.getPointerId(0);
-                mWasLongPress = false;
 
                 if (mDisallowIntercept == null) {
                     mDisallowIntercept = new DisallowIntercept();
@@ -243,7 +236,6 @@
         e.recycle();
 
         mForwarding = true;
-        mWasLongPress = true;
     }
 
     /**
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 258424a..ef6628a 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -1752,7 +1752,8 @@
             boolean validSolution = true;
             // do a binary search to find the max delta that won't conflict with constraints
             while(deltaMin < deltaMax) {
-                final int delta = (deltaMin + deltaMax) / 2;
+                // cast to long to prevent overflow.
+                final int delta = (int) (((long) deltaMin + deltaMax) / 2);
                 invalidateValues();
                 shareOutDelta(delta, totalWeight);
                 validSolution = solve(getArcs(), a, false);
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index b95bc28..595adc2 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -16,18 +16,20 @@
 
 package android.widget;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
+import com.android.internal.R;
+import com.android.internal.view.menu.ShowableListMenu;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.IntProperty;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -38,13 +40,7 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.WindowManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-
-import com.android.internal.R;
-import com.android.internal.view.menu.ShowableListMenu;
-import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
-
-import java.util.Locale;
+import android.widget.AdapterView.OnItemSelectedListener;
 
 /**
  * A ListPopupWindow anchors itself to a host view and displays a
@@ -109,8 +105,6 @@
 
     private boolean mModal;
 
-    private int mLayoutDirection;
-
     PopupWindow mPopup;
 
     /**
@@ -174,7 +168,7 @@
      * 
      * @param context Context used for contained views.
      */
-    public ListPopupWindow(Context context) {
+    public ListPopupWindow(@NonNull Context context) {
         this(context, null, com.android.internal.R.attr.listPopupWindowStyle, 0);
     }
 
@@ -185,7 +179,7 @@
      * @param context Context used for contained views.
      * @param attrs Attributes from inflating parent views used to style the popup.
      */
-    public ListPopupWindow(Context context, AttributeSet attrs) {
+    public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.listPopupWindowStyle, 0);
     }
 
@@ -197,7 +191,8 @@
      * @param attrs Attributes from inflating parent views used to style the popup.
      * @param defStyleAttr Default style attribute to use for popup content.
      */
-    public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+    public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
@@ -210,7 +205,8 @@
      * @param defStyleAttr Style attribute to read for default styling of popup content.
      * @param defStyleRes Style resource ID to use for default styling of popup content.
      */
-    public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
         mContext = context;
         mHandler = new Handler(context.getMainLooper());
 
@@ -227,9 +223,6 @@
 
         mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-        // Set the default layout direction to match the default locale one
-        final Locale locale = mContext.getResources().getConfiguration().locale;
-        mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
     }
 
     /**
@@ -238,7 +231,7 @@
      *
      * @param adapter The adapter to use to create this window's content.
      */
-    public void setAdapter(ListAdapter adapter) {
+    public void setAdapter(@Nullable ListAdapter adapter) {
         if (mObserver == null) {
             mObserver = new PopupDataSetObserver();
         } else if (mAdapter != null) {
@@ -371,7 +364,7 @@
     /**
      * @return The background drawable for the popup window.
      */
-    public Drawable getBackground() {
+    public @Nullable Drawable getBackground() {
         return mPopup.getBackground();
     }
 
@@ -380,7 +373,7 @@
      * 
      * @param d A drawable to set as the background.
      */
-    public void setBackgroundDrawable(Drawable d) {
+    public void setBackgroundDrawable(@Nullable Drawable d) {
         mPopup.setBackgroundDrawable(d);
     }
 
@@ -389,7 +382,7 @@
      * 
      * @param animationStyle Animation style to use.
      */
-    public void setAnimationStyle(int animationStyle) {
+    public void setAnimationStyle(@StyleRes int animationStyle) {
         mPopup.setAnimationStyle(animationStyle);
     }
 
@@ -399,7 +392,7 @@
      * 
      * @return Animation style that will be used.
      */
-    public int getAnimationStyle() {
+    public @StyleRes int getAnimationStyle() {
         return mPopup.getAnimationStyle();
     }
 
@@ -408,7 +401,7 @@
      * 
      * @return The popup's anchor view
      */
-    public View getAnchorView() {
+    public @Nullable View getAnchorView() {
         return mDropDownAnchorView;
     }
 
@@ -418,7 +411,7 @@
      * 
      * @param anchor The view to use as an anchor.
      */
-    public void setAnchorView(View anchor) {
+    public void setAnchorView(@Nullable View anchor) {
         mDropDownAnchorView = anchor;
     }
 
@@ -537,7 +530,7 @@
      * 
      * @see ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
      */
-    public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
+    public void setOnItemClickListener(@Nullable AdapterView.OnItemClickListener clickListener) {
         mItemClickListener = clickListener;
     }
 
@@ -546,9 +539,9 @@
      * 
      * @param selectedListener Listener to register.
      * 
-     * @see ListView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
+     * @see ListView#setOnItemSelectedListener(OnItemSelectedListener)
      */
-    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener selectedListener) {
+    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener selectedListener) {
         mItemSelectedListener = selectedListener;
     }
 
@@ -558,7 +551,7 @@
      * 
      * @param prompt View to use as an informational prompt.
      */
-    public void setPromptView(View prompt) {
+    public void setPromptView(@Nullable View prompt) {
         boolean showing = isShowing();
         if (showing) {
             removePromptView();
@@ -686,7 +679,7 @@
      *
      * @param listener Listener that will be notified when the popup is dismissed.
      */
-    public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+    public void setOnDismissListener(@Nullable PopupWindow.OnDismissListener listener) {
         mPopup.setOnDismissListener(listener);
     }
 
@@ -795,7 +788,7 @@
     /**
      * @return The currently selected item or null if the popup is not showing.
      */
-    public Object getSelectedItem() {
+    public @Nullable Object getSelectedItem() {
         if (!isShowing()) {
             return null;
         }
@@ -834,7 +827,7 @@
      * 
      * @see ListView#getSelectedView()
      */
-    public View getSelectedView() {
+    public @Nullable View getSelectedView() {
         if (!isShowing()) {
             return null;
         }
@@ -846,11 +839,11 @@
      * Only valid when {@link #isShowing()} == {@code true}.
      */
     @Override
-    public ListView getListView() {
+    public @Nullable ListView getListView() {
         return mDropDownList;
     }
 
-    DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
+    @NonNull DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
         return new DropDownListView(context, hijackFocus);
     }
 
@@ -874,7 +867,7 @@
      * 
      * @see #setModal(boolean)
      */
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
+    public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
         // when the drop down is shown, we drive it directly
         if (isShowing()) {
             // the key events are forwarded to the list in the drop down view
@@ -969,7 +962,7 @@
      * 
      * @see #setModal(boolean)
      */
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
+    public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
         if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
             boolean consumed = mDropDownList.onKeyUp(keyCode, event);
             if (consumed && KeyEvent.isConfirmKey(keyCode)) {
@@ -993,7 +986,7 @@
      * 
      * @see #setModal(boolean)
      */
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
             // special case for the back key, we do not even try to send it
             // to the drop down list but instead, consume it immediately
@@ -1159,7 +1152,6 @@
 
             mPopup.setContentView(dropDownView);
         } else {
-            dropDownView = (ViewGroup) mPopup.getContentView();
             final View view = mPromptView;
             if (view != null) {
                 LinearLayout.LayoutParams hintParams =
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index c79e184..09cf704 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -271,6 +271,10 @@
                         attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
                 mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth,
                         ViewGroup.LayoutParams.WRAP_CONTENT);
+                if (pa.hasValueOrEmpty(R.styleable.Spinner_dropDownSelector)) {
+                    popup.setListSelector(pa.getDrawable(
+                            R.styleable.Spinner_dropDownSelector));
+                }
                 popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground));
                 popup.setPromptText(a.getString(R.styleable.Spinner_prompt));
                 pa.recycle();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index acbf5eb..06daf61 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1946,6 +1946,9 @@
     public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
         mActionMenuPresenterCallback = pcb;
         mMenuBuilderCallback = mcb;
+        if (mMenuView != null) {
+            mMenuView.setMenuCallbacks(pcb, mcb);
+        }
     }
 
     /**
@@ -2070,7 +2073,7 @@
         MenuItemImpl mCurrentExpandedItem;
 
         @Override
-        public void initForMenu(Context context, MenuBuilder menu) {
+        public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
             // Clear the expanded action view when menus change.
             if (mMenu != null && mCurrentExpandedItem != null) {
                 mMenu.collapseItemActionView(mCurrentExpandedItem);
diff --git a/core/java/com/android/internal/app/ConfirmUserCreationActivity.java b/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
new file mode 100644
index 0000000..df9cf43
--- /dev/null
+++ b/core/java/com/android/internal/app/ConfirmUserCreationActivity.java
@@ -0,0 +1,134 @@
+/*
+ * 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.internal.app;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.R;
+
+/**
+ * Activity to confirm with the user that it is ok to create a new user, as requested by
+ * an app. It has to do some checks to decide what kind of prompt the user should be shown.
+ * Particularly, it needs to check if the account requested already exists on another user.
+ */
+public class ConfirmUserCreationActivity extends AlertActivity
+        implements DialogInterface.OnClickListener {
+
+    private static final String TAG = "CreateUser";
+
+    private String mUserName;
+    private String mAccountName;
+    private String mAccountType;
+    private PersistableBundle mAccountOptions;
+    private boolean mCanProceed;
+    private UserManager mUserManager;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        Intent intent = getIntent();
+        mUserName = intent.getStringExtra(UserManager.EXTRA_USER_NAME);
+        mAccountName = intent.getStringExtra(UserManager.EXTRA_USER_ACCOUNT_NAME);
+        mAccountType = intent.getStringExtra(UserManager.EXTRA_USER_ACCOUNT_TYPE);
+        mAccountOptions = (PersistableBundle)
+                intent.getParcelableExtra(UserManager.EXTRA_USER_ACCOUNT_OPTIONS);
+
+        mUserManager = getSystemService(UserManager.class);
+
+        String message = checkUserCreationRequirements();
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mMessage = message;
+        ap.mPositiveButtonText = getString(android.R.string.ok);
+        ap.mPositiveButtonListener = this;
+
+        // Show the negative button if the user actually has a choice
+        if (mCanProceed) {
+            ap.mNegativeButtonText = getString(android.R.string.cancel);
+            ap.mNegativeButtonListener = this;
+        }
+        setupAlert();
+    }
+
+    private String checkUserCreationRequirements() {
+        final String callingPackage = getCallingPackage();
+        if (callingPackage == null) {
+            throw new SecurityException(
+                    "User Creation intent must be launched with startActivityForResult");
+        }
+        final ApplicationInfo appInfo;
+        try {
+            appInfo = getPackageManager().getApplicationInfo(callingPackage, 0);
+        } catch (NameNotFoundException nnfe) {
+            throw new SecurityException(
+                    "Cannot find the calling package");
+        }
+        final String message;
+        // Check the user restrictions
+        boolean cantCreateUser = mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER);
+        // Check the system state and user count
+        boolean cantCreateAnyMoreUsers = !mUserManager.canAddMoreUsers();
+        // Check the account existence
+        final Account account = new Account(mAccountName, mAccountType);
+        boolean accountExists = mAccountName != null && mAccountType != null
+                && (AccountManager.get(this).someUserHasAccount(account)
+                    | mUserManager.someUserHasSeedAccount(mAccountName, mAccountType));
+        mCanProceed = true;
+        final String appName = appInfo.loadLabel(getPackageManager()).toString();
+        if (cantCreateUser) {
+            message = getString(R.string.user_creation_cannot_add, appName);
+            mCanProceed = false;
+        } else if (cantCreateAnyMoreUsers) {
+            message = getString(R.string.user_creation_cannot_add_any_more, appName);
+            mCanProceed = false;
+        } else if (accountExists) {
+            message = getString(R.string.user_creation_account_exists, appName, mAccountName);
+        } else {
+            message = getString(R.string.user_creation_adding, appName, mAccountName);
+        }
+        return message;
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        setResult(RESULT_CANCELED);
+        if (which == BUTTON_POSITIVE && mCanProceed) {
+            Log.i(TAG, "Ok, creating user");
+            UserInfo user = mUserManager.createUser(mUserName, 0);
+            if (user == null) {
+                Log.e(TAG, "Couldn't create user");
+                finish();
+                return;
+            }
+            mUserManager.setSeedAccountData(user.id, mAccountName, mAccountType, mAccountOptions);
+            setResult(RESULT_OK);
+        }
+        finish();
+    }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ba0912a..aa38de7 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -718,9 +718,9 @@
 
                     if (ri.handleAllWebDataURI) {
                         // Set default Browser if needed
-                        final String packageName = pm.getDefaultBrowserPackageName(userId);
+                        final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId);
                         if (TextUtils.isEmpty(packageName)) {
-                            pm.setDefaultBrowserPackageName(ri.activityInfo.packageName, userId);
+                            pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId);
                         }
                     } else {
                         // Update Domain Verification status
@@ -737,7 +737,7 @@
                                 categories.contains(Intent.CATEGORY_BROWSABLE);
 
                         if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
-                            pm.updateIntentVerificationStatus(packageName,
+                            pm.updateIntentVerificationStatusAsUser(packageName,
                                     PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
                                     userId);
                         }
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 4b6e7e4..575ef02 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -483,6 +483,12 @@
         return true;
     }
 
+    @Override
+    public void onDestroy() {
+        // Remove any invalidation callbacks
+        mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator);
+    }
+
     public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
         mMenuVisibilityListeners.add(listener);
     }
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index bfc56db..b0b25d3 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -27,7 +27,6 @@
  */
 public class MetricsLogger implements MetricsConstants {
     // Temporary constants go here, to await migration to MetricsConstants.
-    public static final int QS_LOCK_TILE = 257;
     public static final int QS_USER_TILE = 258;
     public static final int QS_BATTERY_TILE = 259;
     public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 260;
@@ -36,24 +35,49 @@
     public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263;
     public static final int ACTION_DEFAULT_SMS_APP_CHANGED = 264;
     public static final int QS_COLOR_MATRIX = 265;
+    public static final int QS_CUSTOM = 266;
+    public static final int ACTION_ZEN_ALLOW_SCREEN_ON = 267;
 
     /**
      * Logged when the user docks a window from recents by longpressing a task and dragging it to
      * the dock area.
      */
-    public static final int ACTION_WINDOW_DOCK_DRAG_DROP = 265;
+    public static final int ACTION_WINDOW_DOCK_DRAG_DROP = 268;
 
     /**
      * Logged when the user docks a fullscreen window by long pressing recents which also opens
      * recents on the lower/right side.
      */
-    public static final int ACTION_WINDOW_DOCK_LONGPRESS = 266;
+    public static final int ACTION_WINDOW_DOCK_LONGPRESS = 269;
 
     /**
      * Logged when the user docks a window by dragging from the navbar which also opens recents on
      * the lower/right side.
      */
-    public static final int ACTION_WINDOW_DOCK_SWIPE = 267;
+    public static final int ACTION_WINDOW_DOCK_SWIPE = 270;
+
+    /**
+     * Logged when the user launches a profile-specific app and we intercept it with the confirm
+     * credentials UI.
+     */
+    public static final int PROFILE_CHALLENGE = 271;
+    public static final int QS_BATTERY_DETAIL = 272;
+
+    /**
+     * Logged when the user goes into the overview history.
+     */
+    public static final int OVERVIEW_HISTORY = 273;
+
+    /**
+     * Logged when the user pages through overview.
+     */
+    public static final int ACTION_OVERVIEW_PAGE = 274;
+
+    /**
+     * Logged when the user launches a task from overview.
+     */
+    public static final int ACTION_OVERVIEW_SELECT = 275;
+
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 830da79..b3222f0 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -19,6 +19,7 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
@@ -29,6 +30,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
 
 /**
  * Represents a connection to {@code installd}. Allows multiple connect and
@@ -61,6 +63,11 @@
     }
 
     public synchronized String transact(String cmd) {
+        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
+            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
+        }
+
         if (!connect()) {
             Slog.e(TAG, "connection failed");
             return "-1";
@@ -96,44 +103,50 @@
         }
     }
 
-    public int execute(String cmd) {
-        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
-                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
-        }
-
-        String res = transact(cmd);
+    public void execute(String cmd, Object... args) throws InstallerException {
+        final String resRaw = executeForResult(cmd, args);
+        int res = -1;
         try {
-            return Integer.parseInt(res);
-        } catch (NumberFormatException ex) {
-            return -1;
+            res = Integer.parseInt(resRaw);
+        } catch (NumberFormatException ignored) {
+        }
+        if (res != 0) {
+            throw new InstallerException(
+                    "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
         }
     }
 
-    public int dexopt(String apkPath, int uid, String instructionSet,
-            int dexoptNeeded, int dexFlags) {
-        return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
-                null /*outputPath*/, dexFlags);
+    public String executeForResult(String cmd, Object... args)
+            throws InstallerException {
+        final StringBuilder builder = new StringBuilder(cmd);
+        for (Object arg : args) {
+            String escaped;
+            if (arg == null) {
+                escaped = "";
+            } else {
+                escaped = String.valueOf(arg);
+            }
+            if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
+                throw new InstallerException(
+                        "Invalid argument while executing " + cmd + " " + Arrays.toString(args));
+            }
+            if (TextUtils.isEmpty(escaped)) {
+                escaped = "!";
+            }
+            builder.append(' ').append(escaped);
+        }
+        return transact(builder.toString());
     }
 
-    public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, String outputPath, int dexFlags) {
-        StringBuilder builder = new StringBuilder("dexopt");
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(instructionSet);
-        builder.append(' ');
-        builder.append(dexoptNeeded);
-        builder.append(' ');
-        builder.append(outputPath != null ? outputPath : "!");
-        builder.append(' ');
-        builder.append(dexFlags);
-        return execute(builder.toString());
+    public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
+            int dexFlags) throws InstallerException {
+        dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags);
+    }
+
+    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+            int dexoptNeeded, String outputPath, int dexFlags) throws InstallerException {
+        execute("dexopt", apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
+                dexFlags);
     }
 
     private boolean connect() {
@@ -227,11 +240,19 @@
 
     public void waitForConnection() {
         for (;;) {
-            if (execute("ping") >= 0) {
+            try {
+                execute("ping");
                 return;
+            } catch (InstallerException ignored) {
             }
             Slog.w(TAG, "installd not ready");
             SystemClock.sleep(1000);
         }
     }
+
+    public static class InstallerException extends Exception {
+        public InstallerException(String detailMessage) {
+            super(detailMessage);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 4a1f7f4..eecc0ee 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -37,6 +37,8 @@
 import android.util.Log;
 import android.webkit.WebViewFactory;
 
+import com.android.internal.os.InstallerConnection.InstallerException;
+
 import dalvik.system.DexFile;
 import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
@@ -502,8 +504,8 @@
                             dexoptNeeded, 0 /*dexFlags*/);
                 }
             }
-        } catch (IOException ioe) {
-            throw new RuntimeException("Error starting system_server", ioe);
+        } catch (IOException | InstallerException e) {
+            throw new RuntimeException("Error starting system_server", e);
         } finally {
             installer.disconnect();
         }
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 1b44ff3..de54d96 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -99,6 +99,9 @@
         mResizingBackgroundDrawable = resizingBackgroundDrawable;
         mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
         mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+        if (mCaptionBackgroundDrawable == null) {
+            mCaptionBackgroundDrawable = mResizingBackgroundDrawable;
+        }
         if (statusBarColor != 0) {
             mStatusBarColor = new ColorDrawable(statusBarColor);
             addSystemBarNodeIfNeeded();
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 40eaaf7..7ae0efb 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -17,18 +17,20 @@
 package com.android.internal.policy;
 
 import com.android.internal.R;
+import com.android.internal.policy.PhoneWindow.PanelFeatureState;
 import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
 import com.android.internal.view.FloatingActionMode;
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.StandaloneActionMode;
 import com.android.internal.view.menu.ContextMenuBuilder;
 import com.android.internal.view.menu.MenuHelper;
-import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.BackgroundFallback;
 import com.android.internal.widget.DecorCaptionView;
 import com.android.internal.widget.FloatingToolbar;
 
+import java.util.List;
+
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.app.ActivityManager;
@@ -49,6 +51,7 @@
 import android.view.Gravity;
 import android.view.InputQueue;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -72,6 +75,7 @@
 import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.View.MeasureSpec.AT_MOST;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.getMode;
@@ -85,6 +89,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
 
 /** @hide */
 public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@@ -194,7 +199,12 @@
     private Drawable mCaptionBackgroundDrawable;
     private Drawable mUserCaptionBackgroundDrawable;
 
-    DecorView(Context context, int featureId, PhoneWindow window) {
+    private float mAvailableWidth;
+
+    String mLogTag = TAG;
+
+    DecorView(Context context, int featureId, PhoneWindow window,
+            WindowManager.LayoutParams params) {
         super(context);
         mFeatureId = featureId;
 
@@ -210,7 +220,11 @@
         mSemiTransparentStatusBarColor = context.getResources().getColor(
                 R.color.system_bar_background_semi_transparent, null /* theme */);
 
+        updateAvailableWidth();
+
         setWindow(window);
+
+        updateLogTag(params);
     }
 
     void setBackgroundFallback(int resId) {
@@ -408,7 +422,7 @@
 
         if (mFeatureId >= 0) {
             if (action == MotionEvent.ACTION_DOWN) {
-                Log.i(TAG, "Watchiing!");
+                Log.i(mLogTag, "Watchiing!");
                 mWatchingForMenu = true;
                 mDownY = (int) event.getY();
                 return false;
@@ -421,7 +435,7 @@
             int y = (int)event.getY();
             if (action == MotionEvent.ACTION_MOVE) {
                 if (y > (mDownY+30)) {
-                    Log.i(TAG, "Closing!");
+                    Log.i(mLogTag, "Closing!");
                     mWindow.closePanel(mFeatureId);
                     mWatchingForMenu = false;
                     return true;
@@ -433,13 +447,13 @@
             return false;
         }
 
-        //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
+        //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
         //        + " (in " + getHeight() + ")");
 
         if (action == MotionEvent.ACTION_DOWN) {
             int y = (int)event.getY();
             if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
-                Log.i(TAG, "Watching!");
+                Log.i(mLogTag, "Watching!");
                 mWatchingForMenu = true;
             }
             return false;
@@ -452,7 +466,7 @@
         int y = (int)event.getY();
         if (action == MotionEvent.ACTION_MOVE) {
             if (y < (getHeight()-30)) {
-                Log.i(TAG, "Opening!");
+                Log.i(mLogTag, "Opening!");
                 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
                         KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
                 mWatchingForMenu = false;
@@ -543,15 +557,15 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
-        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+        final boolean isPortrait =
+                getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
 
         final int widthMode = getMode(widthMeasureSpec);
         final int heightMode = getMode(heightMeasureSpec);
 
         boolean fixedWidth = false;
         if (widthMode == AT_MOST) {
-            final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
-                    : mWindow.mFixedWidthMajor;
+            final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
             if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
                 final int w;
                 if (tvw.type == TypedValue.TYPE_DIMENSION) {
@@ -623,7 +637,7 @@
                 if (tv.type == TypedValue.TYPE_DIMENSION) {
                     min = (int)tv.getDimension(metrics);
                 } else if (tv.type == TypedValue.TYPE_FRACTION) {
-                    min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
+                    min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
                 } else {
                     min = 0;
                 }
@@ -1217,7 +1231,7 @@
                     int fop = fg.getOpacity();
                     int bop = bg.getOpacity();
                     if (false)
-                        Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
+                        Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
                     if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
                         opacity = PixelFormat.OPAQUE;
                     } else if (fop == PixelFormat.UNKNOWN) {
@@ -1232,16 +1246,16 @@
                     // frame with padding... there is no way to tell if the
                     // frame and background together will draw all pixels.
                     if (false)
-                        Log.v(TAG, "Padding: " + mFramePadding);
+                        Log.v(mLogTag, "Padding: " + mFramePadding);
                     opacity = PixelFormat.TRANSLUCENT;
                 }
             }
             if (false)
-                Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
+                Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
         }
 
         if (false)
-            Log.v(TAG, "Selected default opacity: " + opacity);
+            Log.v(mLogTag, "Selected default opacity: " + opacity);
 
         mDefaultOpacity = opacity;
         if (mFeatureId < 0) {
@@ -1600,20 +1614,15 @@
                 enableCaption(StackId.hasWindowDecor(workspaceId));
             }
         }
+        updateAvailableWidth();
         initializeElevation();
     }
 
     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
         mStackId = getStackId();
 
-        mResizingBackgroundDrawable = getResizingBackgroundDrawable(
-                mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
-        if (mCaptionBackgroundDrawable == null) {
-            mCaptionBackgroundDrawable = getContext().getDrawable(
-                    R.drawable.decor_caption_title_focused);
-        }
-
         if (mBackdropFrameRenderer != null) {
+            loadBackgroundDrawablesIfNeeded();
             mBackdropFrameRenderer.onResourcesLoaded(
                     this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
@@ -1635,6 +1644,17 @@
         initializeElevation();
     }
 
+    private void loadBackgroundDrawablesIfNeeded() {
+        if (mResizingBackgroundDrawable == null) {
+            mResizingBackgroundDrawable = getResizingBackgroundDrawable(
+                    mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
+        }
+        if (mCaptionBackgroundDrawable == null) {
+            mCaptionBackgroundDrawable = getContext().getDrawable(
+                    R.drawable.decor_caption_title_focused);
+        }
+    }
+
     // Free floating overlapping windows require a caption.
     private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
         DecorCaptionView decorCaptionView = null;
@@ -1744,7 +1764,7 @@
 
         // We shouldn't really get here as the background fallback should be always available since
         // it is defaulted by the system.
-        Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + mWindow);
+        Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
         return null;
     }
 
@@ -1761,7 +1781,7 @@
             try {
                 workspaceId = callback.getWindowStackId();
             } catch (RemoteException ex) {
-                Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
+                Log.e(mLogTag, "Failed to get the workspace ID of a PhoneWindow.");
             }
         }
         if (workspaceId == INVALID_STACK_ID) {
@@ -1805,6 +1825,7 @@
         }
         final ThreadedRenderer renderer = getHardwareRenderer();
         if (renderer != null) {
+            loadBackgroundDrawablesIfNeeded();
             mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
@@ -1927,6 +1948,34 @@
         }
     }
 
+    void updateLogTag(WindowManager.LayoutParams params) {
+        final String[] split = params.getTitle().toString().split("\\.");
+        if (split.length > 0) {
+            mLogTag = TAG + "[" + split[split.length - 1] + "]";
+        }
+    }
+
+    private void updateAvailableWidth() {
+        Resources res = getResources();
+        mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list) {
+        final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
+        if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) {
+            try {
+                mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu);
+            } catch (AbstractMethodError e) {
+                // We run into this if the app is using supportlib.
+            }
+        }
+    }
+
     private static class ColorViewState {
         View view = null;
         int targetVisibility = View.INVISIBLE;
@@ -1988,12 +2037,12 @@
                 isPrimary = mode == mPrimaryActionMode;
                 isFloating = mode == mFloatingActionMode;
                 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
-                    Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
+                    Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
                             + mode + " was not the current primary action mode! Expected "
                             + mPrimaryActionMode);
                 }
                 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
-                    Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
+                    Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
                             + mode + " was not the current floating action mode! Expected "
                             + mFloatingActionMode);
                 }
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
new file mode 100644
index 0000000..59a1e4a
--- /dev/null
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+
+import java.util.ArrayList;
+
+/**
+ * Calculates the snap targets and the snap position given a position and a velocity. All positions
+ * here are to be interpreted as the left/top edge of the divider rectangle.
+ *
+ * @hide
+ */
+public class DividerSnapAlgorithm {
+
+    /**
+     * 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio
+     */
+    private static final int SNAP_MODE_16_9 = 0;
+
+    /**
+     * 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
+     */
+    private static final int SNAP_FIXED_RATIO = 1;
+
+    /**
+     * 1 snap target: 1:1
+     */
+    private static final int SNAP_ONLY_1_1 = 2;
+
+    private final float mMinFlingVelocityPxPerSecond;
+    private final int mDisplayWidth;
+    private final int mDisplayHeight;
+    private final int mDividerSize;
+    private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
+    private final Rect mInsets = new Rect();
+    private final int mSnapMode;
+    private final float mFixedRatio;
+
+    /** The first target which is still splitting the screen */
+    private final SnapTarget mFirstSplitTarget;
+
+    /** The last target which is still splitting the screen */
+    private final SnapTarget mLastSplitTarget;
+
+    private final SnapTarget mDismissStartTarget;
+    private final SnapTarget mDismissEndTarget;
+    private final SnapTarget mMiddleTarget;
+
+    public DividerSnapAlgorithm(Resources res, float minFlingVelocityPxPerSecond,
+            int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision,
+            Rect insets) {
+        mMinFlingVelocityPxPerSecond = minFlingVelocityPxPerSecond;
+        mDividerSize = dividerSize;
+        mDisplayWidth = displayWidth;
+        mDisplayHeight = displayHeight;
+        mInsets.set(insets);
+        mSnapMode = res.getInteger(
+                com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+        mFixedRatio = res.getFraction(
+                com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
+        calculateTargets(isHorizontalDivision);
+        mFirstSplitTarget = mTargets.get(1);
+        mLastSplitTarget = mTargets.get(mTargets.size() - 2);
+        mDismissStartTarget = mTargets.get(0);
+        mDismissEndTarget = mTargets.get(mTargets.size() - 1);
+        mMiddleTarget = mTargets.get(mTargets.size() / 2);
+    }
+
+    public SnapTarget calculateSnapTarget(int position, float velocity) {
+        if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) {
+            return snap(position);
+        }
+        if (position < mFirstSplitTarget.position && velocity < 0) {
+            return mDismissStartTarget;
+        }
+        if (position > mLastSplitTarget.position && velocity > 0) {
+            return mDismissEndTarget;
+        }
+        if (velocity < 0) {
+            return mFirstSplitTarget;
+        } else {
+            return mLastSplitTarget;
+        }
+    }
+
+    public SnapTarget calculateNonDismissingSnapTarget(int position) {
+        SnapTarget target = snap(position);
+        if (target == mDismissStartTarget) {
+            return mFirstSplitTarget;
+        } else if (target == mDismissEndTarget) {
+            return mLastSplitTarget;
+        } else {
+            return target;
+        }
+    }
+
+    public float calculateDismissingFraction(int position) {
+        if (position < mFirstSplitTarget.position) {
+            return 1f - (float) position / mFirstSplitTarget.position;
+        } else if (position > mLastSplitTarget.position) {
+            return (float) (position - mLastSplitTarget.position)
+                    / (mDismissEndTarget.position - mLastSplitTarget.position);
+        }
+        return 0f;
+    }
+
+    public SnapTarget getClosestDismissTarget(int position) {
+        if (position - mDismissStartTarget.position < mDismissEndTarget.position - position) {
+            return mDismissStartTarget;
+        } else {
+            return mDismissEndTarget;
+        }
+    }
+
+    public SnapTarget getFirstSplitTarget() {
+        return mFirstSplitTarget;
+    }
+
+    public SnapTarget getLastSplitTarget() {
+        return mLastSplitTarget;
+    }
+
+    public SnapTarget getDismissStartTarget() {
+        return mDismissStartTarget;
+    }
+
+    public SnapTarget getDismissEndTarget() {
+        return mDismissEndTarget;
+    }
+
+    private SnapTarget snap(int position) {
+        int minIndex = -1;
+        int minDistance = Integer.MAX_VALUE;
+        int size = mTargets.size();
+        for (int i = 0; i < size; i++) {
+            int distance = Math.abs(position - mTargets.get(i).position);
+            if (distance < minDistance) {
+                minIndex = i;
+                minDistance = distance;
+            }
+        }
+        return mTargets.get(minIndex);
+    }
+
+    private void calculateTargets(boolean isHorizontalDivision) {
+        mTargets.clear();
+        int dividerMax = isHorizontalDivision
+                ? mDisplayHeight
+                : mDisplayWidth;
+        mTargets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START));
+        switch (mSnapMode) {
+            case SNAP_MODE_16_9:
+                addRatio16_9Targets(isHorizontalDivision);
+                break;
+            case SNAP_FIXED_RATIO:
+                addFixedDivisionTargets(isHorizontalDivision);
+                break;
+            case SNAP_ONLY_1_1:
+                addMiddleTarget(isHorizontalDivision);
+                break;
+        }
+        mTargets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END));
+    }
+
+    private void addFixedDivisionTargets(boolean isHorizontalDivision) {
+        int start = isHorizontalDivision ? mInsets.top : mInsets.left;
+        int end = isHorizontalDivision
+                ? mDisplayHeight - mInsets.bottom
+                : mDisplayWidth - mInsets.right;
+        mTargets.add(new SnapTarget((int) (start + mFixedRatio * (end - start)) - mDividerSize / 2,
+                SnapTarget.FLAG_NONE));
+        addMiddleTarget(isHorizontalDivision);
+        mTargets.add(new SnapTarget((int) (start + (1 - mFixedRatio) * (end - start))
+                - mDividerSize / 2, SnapTarget.FLAG_NONE));
+    }
+
+    private void addRatio16_9Targets(boolean isHorizontalDivision) {
+        int start = isHorizontalDivision ? mInsets.top : mInsets.left;
+        int end = isHorizontalDivision
+                ? mDisplayHeight - mInsets.bottom
+                : mDisplayWidth - mInsets.right;
+        int startOther = isHorizontalDivision ? mInsets.left : mInsets.top;
+        int endOther = isHorizontalDivision
+                ? mDisplayWidth - mInsets.right
+                : mDisplayHeight - mInsets.bottom;
+        float size = 9.0f / 16.0f * (endOther - startOther);
+        int sizeInt = (int) Math.floor(size);
+        mTargets.add(new SnapTarget(start + sizeInt, SnapTarget.FLAG_NONE));
+        addMiddleTarget(isHorizontalDivision);
+        mTargets.add(new SnapTarget(end - sizeInt - mDividerSize, SnapTarget.FLAG_NONE));
+    }
+
+    private void addMiddleTarget(boolean isHorizontalDivision) {
+        mTargets.add(new SnapTarget(DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+                mInsets, mDisplayWidth, mDisplayHeight, mDividerSize), SnapTarget.FLAG_NONE));
+    }
+
+    public SnapTarget getMiddleTarget() {
+        return mMiddleTarget;
+    }
+
+    /**
+     * Represents a snap target for the divider.
+     */
+    public static class SnapTarget {
+        public static final int FLAG_NONE = 0;
+
+        /** If the divider reaches this value, the left/top task should be dismissed. */
+        public static final int FLAG_DISMISS_START = 1;
+
+        /** If the divider reaches this value, the right/bottom task should be dismissed */
+        public static final int FLAG_DISMISS_END = 2;
+
+        public final int position;
+        public final int flag;
+
+        public SnapTarget(int position, int flag) {
+            this.position = position;
+            this.flag = flag;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
new file mode 100644
index 0000000..f7f5f15
--- /dev/null
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.graphics.Rect;
+import android.view.WindowManager;
+
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+/**
+ * Utility functions for docked stack divider used by both window manager and System UI.
+ *
+ * @hide
+ */
+public class DockedDividerUtils {
+
+    public static void calculateBoundsForPosition(int position, int dockSide, Rect outRect,
+            int displayWidth, int displayHeight, int dividerSize) {
+        outRect.set(0, 0, displayWidth, displayHeight);
+        switch (dockSide) {
+            case WindowManager.DOCKED_LEFT:
+                outRect.right = position;
+                break;
+            case WindowManager.DOCKED_TOP:
+                outRect.bottom = position;
+                break;
+            case WindowManager.DOCKED_RIGHT:
+                outRect.left = position + dividerSize;
+                break;
+            case WindowManager.DOCKED_BOTTOM:
+                outRect.top = position + dividerSize;
+                break;
+        }
+        if (outRect.left >= outRect.right) {
+            outRect.left = outRect.right - 1;
+        }
+        if (outRect.top >= outRect.bottom) {
+            outRect.top = outRect.bottom - 1;
+        }
+        if (outRect.right <= outRect.left) {
+            outRect.right = outRect.left + 1;
+        }
+        if (outRect.bottom <= outRect.top) {
+            outRect.bottom = outRect.top + 1;
+        }
+    }
+
+    public static int calculatePositionForBounds(Rect bounds, int dockSide, int dividerSize) {
+        switch (dockSide) {
+            case WindowManager.DOCKED_LEFT:
+                return bounds.right;
+            case WindowManager.DOCKED_TOP:
+                return bounds.bottom;
+            case WindowManager.DOCKED_RIGHT:
+                return bounds.left - dividerSize;
+            case WindowManager.DOCKED_BOTTOM:
+                return bounds.top - dividerSize;
+            default:
+                return 0;
+        }
+    }
+
+    public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets,
+            int displayWidth, int displayHeight, int dividerSize) {
+        int start = isHorizontalDivision ? insets.top : insets.left;
+        int end = isHorizontalDivision
+                ? displayHeight - insets.bottom
+                : displayWidth - insets.right;
+        return start + (end - start) / 2 - dividerSize / 2;
+    }
+
+    public static int getDockSideFromCreatedMode(boolean dockOnTopOrLeft,
+            boolean isHorizontalDivision) {
+        if (dockOnTopOrLeft) {
+            if (isHorizontalDivision) {
+                return DOCKED_TOP;
+            } else {
+                return DOCKED_LEFT;
+            }
+        } else {
+            if (isHorizontalDivision) {
+                return DOCKED_BOTTOM;
+            } else {
+                return DOCKED_RIGHT;
+            }
+        }
+    }
+
+    public static int invertDockSide(int dockSide) {
+        switch (dockSide) {
+            case WindowManager.DOCKED_LEFT:
+                return WindowManager.DOCKED_RIGHT;
+            case WindowManager.DOCKED_TOP:
+                return WindowManager.DOCKED_BOTTOM;
+            case WindowManager.DOCKED_RIGHT:
+                return WindowManager.DOCKED_LEFT;
+            case WindowManager.DOCKED_BOTTOM:
+                return WindowManager.DOCKED_TOP;
+            default:
+                return WindowManager.DOCKED_INVALID;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index fafe3d1..4670cca 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -113,6 +113,8 @@
 
     private final static String TAG = "PhoneWindow";
 
+    private static final boolean DEBUG = false;
+
     private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
 
     private static final int CUSTOM_TITLE_COMPATIBLE_FEATURES = DEFAULT_FEATURES |
@@ -146,6 +148,9 @@
     // This is the view in which the window contents are placed. It is either
     // mDecor itself, or a child of mDecor where the contents go.
     ViewGroup mContentParent;
+    // Whether the client has explicitly set the content view. If false and mContentParent is not
+    // null, then the content parent was set due to window preservation.
+    private boolean mContentParentExplicitlySet = false;
 
     Callback2 mTakeSurfaceCallback;
 
@@ -315,7 +320,7 @@
 
     @Override
     public boolean requestFeature(int featureId) {
-        if (mContentParent != null) {
+        if (mContentParentExplicitlySet) {
             throw new AndroidRuntimeException("requestFeature() must be called before adding content");
         }
         final int features = getFeatures();
@@ -399,6 +404,7 @@
         if (cb != null && !isDestroyed()) {
             cb.onContentChanged();
         }
+        mContentParentExplicitlySet = true;
     }
 
     @Override
@@ -429,6 +435,7 @@
         if (cb != null && !isDestroyed()) {
             cb.onContentChanged();
         }
+        mContentParentExplicitlySet = true;
     }
 
     @Override
@@ -677,6 +684,13 @@
         }
     }
 
+    @Override
+    public void reportActivityRelaunched() {
+        if (mDecor != null && mDecor.getViewRootImpl() != null) {
+            mDecor.getViewRootImpl().reportActivityRelaunched();
+        }
+    }
+
     private static void clearMenuViews(PanelFeatureState st) {
         // This can be called on config changes, so we should make sure
         // the views will be reconstructed based on the new orientation, etc.
@@ -2281,7 +2295,7 @@
         } else {
             context = getContext();
         }
-        return new DecorView(context, featureId, this);
+        return new DecorView(context, featureId, this, getAttributes());
     }
 
     protected ViewGroup generateLayout(DecorView decor) {
@@ -2360,6 +2374,8 @@
 
         a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
         a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
+        if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()
+                + ", major: " + mMinWidthMajor.coerceToString());
         if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {
             if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
             a.getValue(R.styleable.Window_windowFixedWidthMajor,
@@ -2668,20 +2684,16 @@
                     invalidatePanelMenu(FEATURE_ACTION_BAR);
                 }
             } else {
-                mTitleView = (TextView)findViewById(R.id.title);
+                mTitleView = (TextView) findViewById(R.id.title);
                 if (mTitleView != null) {
-                    mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
                     if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
-                        View titleContainer = findViewById(
-                                R.id.title_container);
+                        final View titleContainer = findViewById(R.id.title_container);
                         if (titleContainer != null) {
                             titleContainer.setVisibility(View.GONE);
                         } else {
                             mTitleView.setVisibility(View.GONE);
                         }
-                        if (mContentParent instanceof FrameLayout) {
-                            ((FrameLayout)mContentParent).setForeground(null);
-                        }
+                        mContentParent.setForeground(null);
                     } else {
                         mTitleView.setText(mTitle);
                     }
@@ -3776,4 +3788,12 @@
     int getDecorCaptionShade() {
         return mDecorCaptionShade;
     }
+
+    @Override
+    public void setAttributes(WindowManager.LayoutParams params) {
+        super.setAttributes(params);
+        if (mDecor != null) {
+            mDecor.updateLogTag(params);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 849d314..632285c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,8 +24,8 @@
 /** @hide */
 oneway interface IStatusBar
 {
-    void setIcon(int index, in StatusBarIcon icon);
-    void removeIcon(int index);
+    void setIcon(String slot, in StatusBarIcon icon);
+    void removeIcon(String slot);
     void disable(int state1, int state2);
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel(String subPanel);
@@ -46,7 +46,7 @@
     void cancelPreloadRecentApps();
     void showScreenPinningRequest();
 
-    void showKeyboardShortcutsMenu();
+    void toggleKeyboardShortcutsMenu();
 
     /**
      * Notifies the status bar that an app transition is pending to delay applying some flags with
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 0a4ad06..32de45c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -44,7 +44,8 @@
 
     // ---- Methods below are for use by the status bar policy services ----
     // You need the STATUS_BAR_SERVICE permission
-    void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
+    void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
+            out List<StatusBarIcon> iconList,
             out int[] switches, out List<IBinder> binders);
     void onPanelRevealed(boolean clearNotificationEffects, int numItems);
     void onPanelHidden();
@@ -68,7 +69,7 @@
     void preloadRecentApps();
     void cancelPreloadRecentApps();
 
-    void showKeyboardShortcutsMenu();
+    void toggleKeyboardShortcutsMenu();
 
     /**
      * Notifies the status bar that an app transition is pending to delay applying some flags with
diff --git a/core/java/com/android/internal/statusbar/StatusBarIconList.java b/core/java/com/android/internal/statusbar/StatusBarIconList.java
deleted file mode 100644
index 478d245..0000000
--- a/core/java/com/android/internal/statusbar/StatusBarIconList.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.statusbar;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.io.PrintWriter;
-
-public class StatusBarIconList implements Parcelable {
-    private String[] mSlots;
-    private StatusBarIcon[] mIcons;
-
-    public StatusBarIconList() {
-    }
-
-    public StatusBarIconList(Parcel in) {
-        readFromParcel(in);
-    }
-    
-    public void readFromParcel(Parcel in) {
-        this.mSlots = in.readStringArray();
-        final int N = in.readInt();
-        if (N < 0) {
-            mIcons = null;
-        } else {
-            mIcons = new StatusBarIcon[N];
-            for (int i=0; i<N; i++) {
-                if (in.readInt() != 0) {
-                    mIcons[i] = new StatusBarIcon(in);
-                }
-            }
-        }
-    }
-
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeStringArray(mSlots);
-        if (mIcons == null) {
-            out.writeInt(-1);
-        } else {
-            final int N = mIcons.length;
-            out.writeInt(N);
-            for (int i=0; i<N; i++) {
-                StatusBarIcon ic = mIcons[i];
-                if (ic == null) {
-                    out.writeInt(0);
-                } else {
-                    out.writeInt(1);
-                    ic.writeToParcel(out, flags);
-                }
-            }
-        }
-    }
-
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Parcelable.Creator that instantiates StatusBarIconList objects
-     */
-    public static final Parcelable.Creator<StatusBarIconList> CREATOR
-            = new Parcelable.Creator<StatusBarIconList>()
-    {
-        public StatusBarIconList createFromParcel(Parcel parcel)
-        {
-            return new StatusBarIconList(parcel);
-        }
-
-        public StatusBarIconList[] newArray(int size)
-        {
-            return new StatusBarIconList[size];
-        }
-    };
-
-    public void defineSlots(String[] slots) {
-        final int N = slots.length;
-        String[] s = mSlots = new String[N];
-        for (int i=0; i<N; i++) {
-            s[i] = slots[i];
-        }
-        mIcons = new StatusBarIcon[N];
-    }
-
-    public int getSlotIndex(String slot) {
-        final int N = mSlots.length;
-        for (int i=0; i<N; i++) {
-            if (slot.equals(mSlots[i])) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    public int size() {
-        return mSlots.length;
-    }
-
-    public void setIcon(int index, StatusBarIcon icon) {
-        mIcons[index] = icon.clone();
-    }
-
-    public void removeIcon(int index) {
-        mIcons[index] = null;
-    }
-
-    public String getSlot(int index) {
-        return mSlots[index];
-    }
-
-    public StatusBarIcon getIcon(int index) {
-        return mIcons[index];
-    }
-
-    public int getViewIndex(int index) {
-        int count = 0;
-        for (int i=0; i<index; i++) {
-            if (mIcons[i] != null) {
-                count++;
-            }
-        }
-        return count;
-    }
-
-    public void copyFrom(StatusBarIconList that) {
-        if (that.mSlots == null) {
-            this.mSlots = null;
-            this.mIcons = null;
-        } else {
-            final int N = that.mSlots.length;
-            this.mSlots = new String[N];
-            this.mIcons = new StatusBarIcon[N];
-            for (int i=0; i<N; i++) {
-                this.mSlots[i] = that.mSlots[i];
-                this.mIcons[i] = that.mIcons[i] != null ? that.mIcons[i].clone() : null;
-            }
-        }
-    }
-
-    public void dump(PrintWriter pw) {
-        final int N = mSlots.length;
-        pw.println("Icon list:");
-        for (int i=0; i<N; i++) {
-            pw.printf("  %2d: (%s) %s\n", i, mSlots[i], mIcons[i]);
-        }
-    }
-}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 16bf9dd..ca1334c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -26,6 +26,7 @@
 
 import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -372,6 +373,10 @@
         return (array != null) ? array.clone() : null;
     }
 
+    public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
+        return (array != null) ? new ArraySet<T>(array) : null;
+    }
+
     public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
         if (cur == null) {
             cur = new ArraySet<>();
@@ -420,6 +425,16 @@
         return (cur != null) ? cur.contains(val) : false;
     }
 
+    public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
+        if (array == null || size == 0) {
+            return null;
+        } else if (array.length == size) {
+            return array;
+        } else {
+            return Arrays.copyOf(array, size);
+        }
+    }
+
     /**
      * Returns true if the two ArrayLists are equal with respect to the objects they contain.
      * The objects must be in the same order and be reference equal (== not .equals()).
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
index f1add27..696667c 100644
--- a/core/java/com/android/internal/util/IndentingPrintWriter.java
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -45,6 +45,8 @@
      */
     private boolean mEmptyLine = true;
 
+    private char[] mSingleChar = new char[1];
+
     public IndentingPrintWriter(Writer writer, String singleIndent) {
         this(writer, singleIndent, -1);
     }
@@ -78,6 +80,24 @@
     }
 
     @Override
+    public void println() {
+        write('\n');
+    }
+
+    @Override
+    public void write(int c) {
+        mSingleChar[0] = (char) c;
+        write(mSingleChar, 0, 1);
+    }
+
+    @Override
+    public void write(String s, int off, int len) {
+        final char[] buf = new char[len];
+        s.getChars(off, len - off, buf, 0);
+        write(buf, 0, len);
+    }
+
+    @Override
     public void write(char[] buf, int offset, int count) {
         final int indentLength = mIndentBuilder.length();
         final int bufferEnd = offset + count;
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index b692a18..381e71f 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -16,6 +16,10 @@
 
 package com.android.internal.util;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.text.TextUtils;
+
 import java.util.Collection;
 
 /**
@@ -31,6 +35,22 @@
     }
 
     /**
+     * Ensures that an string reference passed as a parameter to the calling
+     * method is not empty.
+     *
+     * @param string an string reference
+     * @return the string reference that was validated
+     * @throws IllegalArgumentException if {@code string} is empty
+     */
+    public static @NonNull String checkStringNotEmpty(final String string,
+            final Object errorMessage) {
+        if (TextUtils.isEmpty(string)) {
+            throw new IllegalArgumentException(String.valueOf(errorMessage));
+        }
+        return string;
+    }
+
+    /**
      * Ensures that an object reference passed as a parameter to the calling
      * method is not null.
      *
@@ -38,7 +58,7 @@
      * @return the non-null reference that was validated
      * @throws NullPointerException if {@code reference} is null
      */
-    public static <T> T checkNotNull(final T reference) {
+    public static @NonNull <T> T checkNotNull(final T reference) {
         if (reference == null) {
             throw new NullPointerException();
         }
@@ -55,7 +75,7 @@
      * @return the non-null reference that was validated
      * @throws NullPointerException if {@code reference} is null
      */
-    public static <T> T checkNotNull(final T reference, final Object errorMessage) {
+    public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
         if (reference == null) {
             throw new NullPointerException(String.valueOf(errorMessage));
         }
@@ -95,7 +115,8 @@
      * @return the validated numeric value
      * @throws IllegalArgumentException if {@code value} was negative
      */
-    public static int checkArgumentNonnegative(final int value, final String errorMessage) {
+    public static @IntRange(from = 0) int checkArgumentNonnegative(final int value,
+            final String errorMessage) {
         if (value < 0) {
             throw new IllegalArgumentException(errorMessage);
         }
@@ -218,6 +239,33 @@
     }
 
     /**
+     * Ensures that the argument long value is within the inclusive range.
+     *
+     * @param value a long value
+     * @param lower the lower endpoint of the inclusive range
+     * @param upper the upper endpoint of the inclusive range
+     * @param valueName the name of the argument to use if the check fails
+     *
+     * @return the validated long value
+     *
+     * @throws IllegalArgumentException if {@code value} was not within the range
+     */
+    public static long checkArgumentInRange(long value, long lower, long upper,
+            String valueName) {
+        if (value < lower) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+        } else if (value > upper) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+        }
+
+        return value;
+    }
+
+    /**
      * Ensures that the array is not {@code null}, and none of its elements are {@code null}.
      *
      * @param value an array of boxed objects
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 406b487..dc66818 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -779,7 +779,7 @@
         @Override
         public final void handleMessage(Message msg) {
             if (!mHasQuit) {
-                if (mSm != null) {
+                if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
                     mSm.onPreHandleMessage(msg);
                 }
 
@@ -807,7 +807,7 @@
                 // We need to check if mSm == null here as we could be quitting.
                 if (mDbg && mSm != null) mSm.log("handleMessage: X");
 
-                if (mSm != null) {
+                if (mSm != null && msg.what != SM_INIT_CMD && msg.what != SM_QUIT_CMD) {
                     mSm.onPostHandleMessage(msg);
                 }
             }
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 8699843..aa4b564c 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -25,6 +25,8 @@
 import android.view.IWindow;
 import android.view.IWindowSession;
 
+import com.android.internal.os.IResultReceiver;
+
 public class BaseIWindow extends IWindow.Stub {
     private IWindowSession mSession;
     public int mSeq;
@@ -103,4 +105,8 @@
     @Override
     public void dispatchWindowShown() {
     }
+
+    @Override
+    public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+    }
 }
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 85ec29c..0e7f06b 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -30,7 +30,7 @@
 
 import java.lang.ref.WeakReference;
 
-public class IInputConnectionWrapper extends IInputContext.Stub {
+public abstract class IInputConnectionWrapper extends IInputContext.Stub {
     static final String TAG = "IInputConnectionWrapper";
 
     private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
@@ -80,15 +80,25 @@
     }
     
     public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
-        mInputConnection = new WeakReference<InputConnection>(conn);
+        mInputConnection = new WeakReference<>(conn);
         mMainLooper = mainLooper;
         mH = new MyHandler(mMainLooper);
     }
 
-    public boolean isActive() {
-        return true;
-    }
-    
+    abstract protected boolean isActive();
+
+    /**
+     * Called when the user took some actions that should be taken into consideration to update the
+     * LRU list for input method rotation.
+     */
+    abstract protected void onUserAction();
+
+    /**
+     * Called when the input method started or stopped full-screen mode.
+     *
+     */
+    abstract protected void onReportFullscreenMode(boolean enabled);
+
     public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
         dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
     }
@@ -284,6 +294,7 @@
                     return;
                 }
                 ic.commitText((CharSequence)msg.obj, msg.arg1);
+                onUserAction();
                 return;
             }
             case DO_SET_SELECTION: {
@@ -338,6 +349,7 @@
                     return;
                 }
                 ic.setComposingText((CharSequence)msg.obj, msg.arg1);
+                onUserAction();
                 return;
             }
             case DO_SET_COMPOSING_REGION: {
@@ -369,6 +381,7 @@
                     return;
                 }
                 ic.sendKeyEvent((KeyEvent)msg.obj);
+                onUserAction();
                 return;
             }
             case DO_CLEAR_META_KEY_STATES: {
@@ -413,7 +426,9 @@
                     Log.w(TAG, "reportFullscreenMode on inexistent InputConnection");
                     return;
                 }
-                ic.reportFullscreenMode(msg.arg1 == 1);
+                final boolean enabled = msg.arg1 == 1;
+                ic.reportFullscreenMode(enabled);
+                onReportFullscreenMode(enabled);
                 return;
             }
             case DO_PERFORM_PRIVATE_COMMAND: {
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
index 92e9ea6..7ac0ac3 100644
--- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.view.menu;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -58,7 +60,7 @@
     }
 
     @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
+    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
         mContext = context;
         mInflater = LayoutInflater.from(mContext);
         mMenu = menu;
diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
index 2439b5d..5223a7b 100644
--- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
@@ -17,6 +17,8 @@
 
 import com.android.internal.view.menu.MenuView.ItemView;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -49,7 +51,7 @@
     }
 
     @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
+    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
         super.initForMenu(context, menu);
         mMaxItems = -1;
     }
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index c476354..2fff3ba 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.view.menu;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -76,7 +78,7 @@
     }
 
     @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
+    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
         if (mThemeRes != 0) {
             mContext = new ContextThemeWrapper(context, mThemeRes);
             mInflater = LayoutInflater.from(mContext);
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 465d775..31b2f96 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -17,6 +17,7 @@
 package com.android.internal.view.menu;
 
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1027,23 +1028,24 @@
         mIsActionItemsStale = true;
         onItemsChanged(true);
     }
-    
+
+    @NonNull
     public ArrayList<MenuItemImpl> getVisibleItems() {
         if (!mIsVisibleItemsStale) return mVisibleItems;
-        
+
         // Refresh the visible items
         mVisibleItems.clear();
-        
+
         final int itemsSize = mItems.size(); 
         MenuItemImpl item;
         for (int i = 0; i < itemsSize; i++) {
             item = mItems.get(i);
             if (item.isVisible()) mVisibleItems.add(item);
         }
-        
+
         mIsVisibleItemsStale = false;
         mIsActionItemsStale = true;
-        
+
         return mVisibleItems;
     }
 
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index 98f5d90..b151f34 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.view.menu;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.view.MenuItem;
 import android.view.View;
@@ -73,7 +75,7 @@
     public abstract void setOnDismissListener(PopupWindow.OnDismissListener listener);
 
     @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
+    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
         // Don't need to do anything; we added as a presenter in the constructor.
     }
 
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index c847c15..65bdc09 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.view.menu;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Parcelable;
 import android.view.ViewGroup;
@@ -49,14 +51,16 @@
     }
 
     /**
-     * Initialize this presenter for the given context and menu.
-     * This method is called by MenuBuilder when a presenter is
-     * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
+     * Initializes this presenter for the given context and menu.
+     * <p>
+     * This method is called by MenuBuilder when a presenter is added. See
+     * {@link MenuBuilder#addMenuPresenter(MenuPresenter)}.
      *
-     * @param context Context for this presenter; used for view creation and resource management
-     * @param menu Menu to host
+     * @param context the context for this presenter; used for view creation
+     *                and resource management, must be non-{@code null}
+     * @param menu the menu to host, or {@code null} to clear the hosted menu
      */
-    public void initForMenu(Context context, MenuBuilder menu);
+    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu);
 
     /**
      * Retrieve a MenuView to display the menu specified in
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index c2adc42..a67e43a 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.internal.view.menu;
 
 import android.content.Context;
@@ -7,7 +23,6 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnKeyListener;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -30,7 +45,7 @@
         MenuPresenter, OnKeyListener {
 
     private final Context mContext;
-    private final LayoutInflater mInflater;
+
     private final MenuBuilder mMenu;
     private final MenuAdapter mAdapter;
     private final boolean mOverflowOnly;
@@ -79,8 +94,6 @@
     private Callback mPresenterCallback;
     private ViewTreeObserver mTreeObserver;
 
-    private ViewGroup mMeasureParent;
-
     /** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
     private boolean mWasDismissed;
 
@@ -99,10 +112,10 @@
     public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
             int popupStyleRes, boolean overflowOnly) {
         mContext = Preconditions.checkNotNull(context);
-        mInflater = LayoutInflater.from(context);
         mMenu = menu;
         mOverflowOnly = overflowOnly;
-        mAdapter = new MenuAdapter(menu, mInflater, mOverflowOnly);
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly);
         mPopupStyleAttr = popupStyleAttr;
         mPopupStyleRes = popupStyleRes;
 
@@ -155,8 +168,7 @@
         mPopup.setDropDownGravity(mDropDownGravity);
 
         if (!mHasContentWidth) {
-            mContentWidth = measureIndividualMenuWidth(
-                    mAdapter, mMeasureParent, mContext, mPopupMaxWidth);
+            mContentWidth = measureIndividualMenuWidth(mAdapter, null, mContext, mPopupMaxWidth);
             mHasContentWidth = true;
         }
 
@@ -177,7 +189,9 @@
                             false);
             TextView titleView = (TextView) titleItemView.findViewById(
                     com.android.internal.R.id.title);
-            titleView.setText(mMenu.getHeaderTitle());
+            if (titleView != null) {
+                titleView.setText(mMenu.getHeaderTitle());
+            }
             titleItemView.setEnabled(false);
             listView.addHeaderView(titleItemView, null, false);
 
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 825e336..f90b59d 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -17,6 +17,8 @@
 package com.android.internal.widget;
 
 import android.animation.LayoutTransition;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActionBar;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -1593,7 +1595,7 @@
         MenuItemImpl mCurrentExpandedItem;
 
         @Override
-        public void initForMenu(Context context, MenuBuilder menu) {
+        public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
             // Clear the expanded action view when menus change.
             if (mMenu != null && mCurrentExpandedItem != null) {
                 mMenu.collapseItemActionView(mCurrentExpandedItem);
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index c3fe9e7..409a17f 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.widget;
 
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -351,7 +349,7 @@
         Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
         if (callback != null) {
             try {
-                callback.changeWindowStack(FULLSCREEN_WORKSPACE_STACK_ID);
+                callback.exitFreeformMode();
             } catch (RemoteException ex) {
                 Log.e(TAG, "Cannot change task workspace.");
             }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e38d82f..4e8f19c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -24,6 +24,7 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.IBinder;
@@ -34,6 +35,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -135,6 +137,8 @@
 
     private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
 
+    private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
+
     // Maximum allowed number of repeated or ordered characters in a sequence before we'll
     // consider it a complex PIN/password.
     public static final int MAX_ALLOWED_SEQUENCE = 3;
@@ -143,6 +147,7 @@
     private final ContentResolver mContentResolver;
     private DevicePolicyManager mDevicePolicyManager;
     private ILockSettings mLockSettingsService;
+    private UserManager mUserManager;
 
 
     public static final class RequestThrottledException extends Exception {
@@ -173,6 +178,13 @@
         return mDevicePolicyManager;
     }
 
+    private UserManager getUserManager() {
+        if (mUserManager == null) {
+            mUserManager = UserManager.get(mContext);
+        }
+        return mUserManager;
+    }
+
     private TrustManager getTrustManager() {
         TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
         if (trust == null) {
@@ -866,6 +878,39 @@
     }
 
     /**
+     * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
+     * for user handles that do not belong to a managed profile.
+     */
+    public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled) {
+        UserInfo info = getUserManager().getUserInfo(userHandle);
+        if (info.isManagedProfile()) {
+            setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userHandle);
+        }
+    }
+
+    /**
+     * Retrieves whether the Separate Profile Challenge is enabled for this {@param userHandle}.
+     */
+    public boolean isSeparateProfileChallengeEnabled(int userHandle) {
+        UserInfo info = getUserManager().getUserInfo(userHandle);
+        if (info == null || !info.isManagedProfile()) {
+            return false;
+        }
+        return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userHandle);
+    }
+
+    /**
+     * Retrieves whether the current DPM allows use of the Profile Challenge.
+     */
+    public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+        UserInfo info = getUserManager().getUserInfo(userHandle);
+        if (info == null || !info.isManagedProfile()) {
+            return false;
+        }
+        return getDevicePolicyManager().isSeparateProfileChallengeAllowed(userHandle);
+    }
+
+    /**
      * Deserialize a pattern.
      * @param string The pattern serialized with {@link #patternToString}
      * @return The pattern.
@@ -1288,10 +1333,6 @@
         }
     }
 
-    public static boolean isSeparateWorkChallengeEnabled() {
-        return StorageManager.isFileBasedEncryptionEnabled();
-    }
-
     public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
         try {
             getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub);
diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
new file mode 100644
index 0000000..ff0e63d
--- /dev/null
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.app.AppGlobals;
+import android.app.backup.BlobBackupHelper;
+import android.content.pm.IPackageManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+public class PermissionBackupHelper extends BlobBackupHelper {
+    private static final String TAG = "PermissionBackup";
+    private static final boolean DEBUG = false;
+
+    // current schema of the backup state blob
+    private static final int STATE_VERSION = 1;
+
+    // key under which the permission-grant state blob is committed to backup
+    private static final String KEY_PERMISSIONS = "permissions";
+
+    public PermissionBackupHelper() {
+        super(STATE_VERSION, KEY_PERMISSIONS);
+    }
+
+    @Override
+    protected byte[] getBackupPayload(String key) {
+        IPackageManager pm = AppGlobals.getPackageManager();
+        if (DEBUG) {
+            Slog.d(TAG, "Handling backup of " + key);
+        }
+        try {
+            switch (key) {
+                case KEY_PERMISSIONS:
+                    return pm.getPermissionGrantBackup(UserHandle.USER_SYSTEM);
+
+                default:
+                    Slog.w(TAG, "Unexpected backup key " + key);
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Unable to store payload " + key);
+        }
+        return null;
+    }
+
+    @Override
+    protected void applyRestoredPayload(String key, byte[] payload) {
+        IPackageManager pm = AppGlobals.getPackageManager();
+        if (DEBUG) {
+            Slog.d(TAG, "Handling restore of " + key);
+        }
+        try {
+            switch (key) {
+                case KEY_PERMISSIONS:
+                    pm.restorePermissionGrants(payload, UserHandle.USER_SYSTEM);
+                    break;
+
+                default:
+                    Slog.w(TAG, "Unexpected restore key " + key);
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to restore key " + key);
+        }
+    }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 234815f..3f76e13 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -46,6 +46,7 @@
     private static final String SYNC_SETTINGS_HELPER = "account_sync_settings";
     private static final String PREFERRED_HELPER = "preferred_activities";
     private static final String NOTIFICATION_HELPER = "notifications";
+    private static final String PERMISSION_HELPER = "permissions";
 
     // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
     // are also used in the full-backup file format, so must not change unless steps are
@@ -94,6 +95,7 @@
         addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
         addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
         addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
+        addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
 
         super.onBackup(oldState, data, newState);
     }
@@ -128,6 +130,7 @@
         addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
         addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
         addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
+        addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
 
         try {
             super.onRestore(data, appVersionCode, newState);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 30593f2..fc1ba44 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -7,6 +7,7 @@
 LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_CFLAGS += -Wno-non-virtual-dtor
 LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
+LOCAL_CFLAGS += -DHWUI_NEW_OPS
 LOCAL_CPPFLAGS += -Wno-conversion-null
 
 ifeq ($(TARGET_ARCH), arm)
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 63f193d..40af22a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1257,6 +1257,7 @@
     REG_JNI(register_android_os_Binder),
     REG_JNI(register_android_os_Parcel),
     REG_JNI(register_android_nio_utils),
+    REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_Graphics),
     REG_JNI(register_android_view_DisplayEventReceiver),
     REG_JNI(register_android_view_RenderNode),
@@ -1289,7 +1290,6 @@
     REG_JNI(register_android_graphics_BitmapRegionDecoder),
     REG_JNI(register_android_graphics_Camera),
     REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
-    REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_CanvasProperty),
     REG_JNI(register_android_graphics_ColorFilter),
     REG_JNI(register_android_graphics_DrawFilter),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index a805b6d..80ccb61 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -774,11 +774,14 @@
     return ret;
 }
 
-static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
-    LocalScopedBitmap bitmap(bitmapHandle);
+static void Bitmap_destruct(Bitmap* bitmap) {
     bitmap->detachFromJava();
 }
 
+static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Bitmap_destruct));
+}
+
 static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmap(bitmapHandle);
     bitmap->freePixels();
@@ -1357,7 +1360,7 @@
         (void*)Bitmap_copy },
     {   "nativeCopyAshmem",         "(J)Landroid/graphics/Bitmap;",
         (void*)Bitmap_copyAshmem },
-    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },
+    {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
     {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
     {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
     {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 4a0f3fc..92f7812 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -260,6 +260,15 @@
         return nullObjectReturn("SkAndroidCodec::NewFromStream returned null");
     }
 
+    // Do not allow ninepatch decodes to 565.  In the past, decodes to 565
+    // would dither, and we do not want to pre-dither ninepatches, since we
+    // know that they will be stretched.  We no longer dither 565 decodes,
+    // but we continue to prevent ninepatches from decoding to 565, in order
+    // to maintain the old behavior.
+    if (peeker.mPatch && kRGB_565_SkColorType == prefColorType) {
+        prefColorType = kN32_SkColorType;
+    }
+
     // Determine the output size and return if the client only wants the size.
     SkISize size = codec->getSampledDimensions(sampleSize);
     if (options != NULL) {
@@ -369,15 +378,7 @@
         case SkCodec::kIncompleteInput:
             break;
         default:
-            return nullObjectReturn("codec->getAndoridPixels() failed.");
-    }
-
-    // Some images may initially report that they have alpha due to the format
-    // of the encoded data, but then never use any colors which have alpha
-    // less than 100%.  Here we check if the image really had alpha, and
-    // mark it as opaque if it is actually opaque.
-    if (kOpaque_SkAlphaType != alphaType && !codec->reallyHasAlpha()) {
-        decodingBitmap.setAlphaType(kOpaque_SkAlphaType);
+            return nullObjectReturn("codec->getAndroidPixels() failed.");
     }
 
     int scaledWidth = size.width();
@@ -587,10 +588,6 @@
     return doDecode(env, stream.release(), NULL, options);
 }
 
-static void nativeRequestCancel(JNIEnv*, jobject joptions) {
-    // Deprecated
-}
-
 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
     return ::lseek64(descriptor, 0, SEEK_CUR) != -1 ? JNI_TRUE : JNI_FALSE;
@@ -630,10 +627,6 @@
     },
 };
 
-static const JNINativeMethod gOptionsMethods[] = {
-    {   "requestCancel", "()V", (void*)nativeRequestCancel }
-};
-
 int register_android_graphics_BitmapFactory(JNIEnv* env) {
     jclass options_class = FindClassOrDie(env, "android/graphics/BitmapFactory$Options");
     gOptions_bitmapFieldID = GetFieldIDOrDie(env, options_class, "inBitmap",
@@ -665,8 +658,6 @@
     gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
                                                         "(IIIIIIIIFIF)V");
 
-    android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory$Options",
-                                  gOptionsMethods, NELEM(gOptionsMethods));
     return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory",
                                          gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 83fd073..1a86e5f 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -42,7 +42,7 @@
     }
 
     static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
-        return reinterpret_cast<jlong>(SkColorFilter::CreateLightingFilter(mul, add));
+        return reinterpret_cast<jlong>(SkColorMatrixFilter::CreateLightingFilter(mul, add));
     }
 
     static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index ab0df55..7c8dbe8 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -36,12 +36,12 @@
 namespace android {
 
 static jlong FontFamily_create(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
-    FontLanguage fontLanguage;
-    if (lang != NULL) {
-        ScopedUtfChars str(env, lang);
-        fontLanguage = FontLanguage(str.c_str(), str.size());
+    if (lang == NULL) {
+        return (jlong)new FontFamily(variant);
     }
-    return (jlong)new FontFamily(fontLanguage, variant);
+    ScopedUtfChars str(env, lang);
+    uint32_t langId = FontStyle::registerLanguageList(str.c_str());
+    return (jlong)new FontFamily(langId, variant);
 }
 
 static void FontFamily_unref(JNIEnv* env, jobject clazz, jlong familyPtr) {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 654d148..98f8ce3 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -76,9 +76,12 @@
         AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
     };
 
-    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        delete obj;
+    static void deletePaint(Paint* paint) {
+        delete paint;
+    }
+
+    static jlong getNativeFinalizer(JNIEnv*, jobject) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deletePaint));
     }
 
     static jlong init(JNIEnv* env, jobject) {
@@ -313,94 +316,10 @@
         obj->setTextAlign(align);
     }
 
-    // generate bcp47 identifier for the supplied locale
-    static void toLanguageTag(char* output, size_t outSize,
-            const char* locale) {
-        if (output == NULL || outSize <= 0) {
-            return;
-        }
-        if (locale == NULL) {
-            output[0] = '\0';
-            return;
-        }
-        char canonicalChars[ULOC_FULLNAME_CAPACITY];
-        UErrorCode uErr = U_ZERO_ERROR;
-        uloc_canonicalize(locale, canonicalChars, ULOC_FULLNAME_CAPACITY,
-                &uErr);
-        if (U_SUCCESS(uErr)) {
-            char likelyChars[ULOC_FULLNAME_CAPACITY];
-            uErr = U_ZERO_ERROR;
-            uloc_addLikelySubtags(canonicalChars, likelyChars,
-                    ULOC_FULLNAME_CAPACITY, &uErr);
-            if (U_SUCCESS(uErr)) {
-                uErr = U_ZERO_ERROR;
-                uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr);
-                if (U_SUCCESS(uErr)) {
-                    return;
-                } else {
-                    ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars,
-                            u_errorName(uErr));
-                }
-            } else {
-                ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s",
-                        canonicalChars, u_errorName(uErr));
-            }
-        } else {
-            ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale,
-                    u_errorName(uErr));
-        }
-        // unable to build a proper language identifier
-        output[0] = '\0';
-    }
-
-    static void toLanguageTags(std::string* output, const char* locales) {
-        if (output == NULL) {
-            return;
-        }
-        if (locales == NULL) {
-            output->clear();
-            return;
-        }
-
-        char langTag[ULOC_FULLNAME_CAPACITY];
-        const char* commaLoc = strchr(locales, ',');
-        if (commaLoc == NULL) {
-            assert(locales[0] != '\0');  // the string should not be empty
-            toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locales);
-            *output = langTag;
-            return;
-        }
-
-        size_t len = strlen(locales);
-        char locale[len];
-        output->clear();
-        output->reserve(len);
-        const char* lastStart = locales;
-        do {
-            assert(lastStart > commaLoc);  // the substring should not be empty
-            strncpy(locale, lastStart, commaLoc - lastStart);
-            locale[commaLoc - lastStart] = '\0';
-            toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
-            if (langTag[0] != '\0') {
-                output->append(langTag);
-                output->push_back(',');
-            }
-            lastStart = commaLoc + 1;
-            commaLoc = strchr(lastStart, ',');
-        } while (commaLoc != NULL);
-        assert(lastStart[0] != '\0');  // the final substring should not be empty
-        toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, lastStart);
-        if (langTag[0] != '\0') {
-            output->append(langTag);
-        }
-    }
-
     static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         ScopedUtfChars localesChars(env, locales);
-        std::string buf;
-        toLanguageTags(&buf, localesChars.c_str());
-        jint minikinLangListId = FontStyle::registerLanguageList(buf);
+        jint minikinLangListId = FontStyle::registerLanguageList(localesChars.c_str());
         obj->setMinikinLangListId(minikinLangListId);
         return minikinLangListId;
     }
@@ -947,7 +866,7 @@
 }; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
-    {"nFinalizer", "(J)V", (void*) PaintGlue::finalizer},
+    {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
     {"nInit","()J", (void*) PaintGlue::init},
     {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
 
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 799ed83..2507e4d 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -61,18 +61,27 @@
     SkASSERT(shaderHandle);
     SkAutoTUnref<SkShader> currentShader(reinterpret_cast<SkShader*>(shaderHandle));
 
-    SkMatrix currentMatrix;
-    SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(&currentMatrix));
+    // Attempt to peel off an existing proxy shader and get the proxy's matrix. If
+    // the proxy existed and it's matrix equals the desired matrix then just return
+    // the proxy, otherwise replace it with a new proxy containing the desired matrix.
+    //
+    // refAsALocalMatrixShader(): if the shader contains a proxy then it unwraps the proxy
+    //                            returning both the underlying shader and the proxy's matrix.
+    // newWithLocalMatrix(): will return a proxy shader that wraps the provided shader and
+    //                       concats the provided local matrix with the shader's matrix.
+    //
+    // WARNING: This proxy replacement only behaves like a setter because the Java
+    //          API enforces that all local matrices are set using this call and
+    //          not passed to the constructor of the Shader.
+    SkMatrix proxyMatrix;
+    SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(&proxyMatrix));
     if (baseShader.get()) {
-        // if the matrices are same then there is no need to allocate a new
-        // shader that is identical to the existing one.
-        if (currentMatrix == *matrix) {
+        if (proxyMatrix == *matrix) {
             return reinterpret_cast<jlong>(currentShader.detach());
         }
-        return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(baseShader, *matrix));
+        return reinterpret_cast<jlong>(baseShader->newWithLocalMatrix(*matrix));
     }
-
-    return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(currentShader, *matrix));
+    return reinterpret_cast<jlong>(currentShader->newWithLocalMatrix(*matrix));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index e4e73a4..34877e0 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -37,8 +37,12 @@
     return reinterpret_cast<Canvas*>(canvasHandle);
 }
 
-static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
-    delete get_canvas(canvasHandle);
+static void delete_canvas(Canvas* canvas) {
+    delete canvas;
+}
+
+static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&delete_canvas));
 }
 
 // Native wrapper constructor used by Canvas(Bitmap)
@@ -710,7 +714,7 @@
 }; // namespace CanvasJNI
 
 static const JNINativeMethod gMethods[] = {
-    {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
+    {"getNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer},
     {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
     {"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
     {"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 6d3c7d7..8e8f6c3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -152,7 +152,8 @@
 
 static struct {
     jmethodID postDynPolicyEventFromNative;
-} gDynPolicyEventHandlerMethods;
+    jmethodID postRecordConfigEventFromNative;
+} gAudioPolicyEventHandlerMethods;
 
 static Mutex gLock;
 
@@ -378,12 +379,26 @@
     const char* zechars = regId.string();
     jstring zestring = env->NewStringUTF(zechars);
 
-    env->CallStaticVoidMethod(clazz, gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative,
+    env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative,
             event, zestring, val);
 
     env->ReleaseStringUTFChars(zestring, zechars);
     env->DeleteLocalRef(clazz);
+}
 
+static void
+android_media_AudioSystem_recording_callback(int event, int session, int source)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    if (env == NULL) {
+        return;
+    }
+
+    jclass clazz = env->FindClass(kClassPathName);
+    env->CallStaticVoidMethod(clazz,
+            gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+            event, session, source);
+    env->DeleteLocalRef(clazz);
 }
 
 static jint
@@ -498,6 +513,22 @@
 }
 
 static jint
+android_media_AudioSystem_setMasterMono(JNIEnv *env, jobject thiz, jboolean mono)
+{
+    return (jint) check_AudioSystem_Command(AudioSystem::setMasterMono(mono));
+}
+
+static jboolean
+android_media_AudioSystem_getMasterMono(JNIEnv *env, jobject thiz)
+{
+    bool mono;
+    if (AudioSystem::getMasterMono(&mono) != NO_ERROR) {
+        mono = false;
+    }
+    return mono;
+}
+
+static jint
 android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
 {
     return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
@@ -1487,6 +1518,12 @@
     AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback);
 }
 
+static void
+android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz)
+{
+    AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback);
+}
+
 
 static jint convertAudioMixToNative(JNIEnv *env,
                                     AudioMix *nAudioMix,
@@ -1637,6 +1674,8 @@
     {"getMasterVolume",     "()F",      (void *)android_media_AudioSystem_getMasterVolume},
     {"setMasterMute",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMute},
     {"getMasterMute",       "()Z",      (void *)android_media_AudioSystem_getMasterMute},
+    {"setMasterMono",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMono},
+    {"getMasterMono",       "()Z",      (void *)android_media_AudioSystem_getMasterMono},
     {"getDevicesForStream", "(I)I",     (void *)android_media_AudioSystem_getDevicesForStream},
     {"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
     {"getPrimaryOutputFrameCount",   "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
@@ -1659,6 +1698,8 @@
                                             (void *)android_media_AudioSystem_registerPolicyMixes},
     {"native_register_dynamic_policy_callback", "()V",
                                     (void *)android_media_AudioSystem_registerDynPolicyCallback},
+    {"native_register_recording_callback", "()V",
+                                    (void *)android_media_AudioSystem_registerRecordingCallback},
     {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
 };
 
@@ -1762,9 +1803,12 @@
     gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
                                                     eventHandlerClass, "mJniCallback", "J");
 
-    gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative =
+    gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative =
             GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
                     "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
+    gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
+            GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
+                    "recordingCallbackFromNative", "(III)V");
 
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7860b74..42f3fb0 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1049,6 +1049,10 @@
     pJniStorage->mDeviceCallback.clear();
 }
 
+static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
+    return FCC_8;
+}
+
 
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
@@ -1106,6 +1110,7 @@
     {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
     {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
     {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
+    {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
 };
 
 
@@ -1135,6 +1140,9 @@
 // ----------------------------------------------------------------------------
 int register_android_media_AudioTrack(JNIEnv *env)
 {
+    // must be first
+    int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+
     javaAudioTrackFields.nativeTrackInJavaObj = NULL;
     javaAudioTrackFields.postNativeEventInJava = NULL;
 
@@ -1173,7 +1181,7 @@
     // initialize PlaybackParams field info
     gPlaybackParamsFields.init(env);
 
-    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+    return res;
 }
 
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 2488111..03a1e71 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -84,6 +84,7 @@
     jfieldID privateClean_field;
     jfieldID sharedClean_field;
     jfieldID swappedOut_field;
+    jfieldID swappedOutPss_field;
 };
 
 struct stat_field_names {
@@ -94,20 +95,22 @@
     const char* privateClean_name;
     const char* sharedClean_name;
     const char* swappedOut_name;
+    const char* swappedOutPss_name;
 };
 
 static stat_fields stat_fields[_NUM_CORE_HEAP];
 
 static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
     { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty",
-        "otherPrivateClean", "otherSharedClean", "otherSwappedOut" },
+        "otherPrivateClean", "otherSharedClean", "otherSwappedOut", "otherSwappedOutPss" },
     { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty",
-        "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut" },
+        "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut", "dalvikSwappedOutPss" },
     { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty",
-        "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut" }
+        "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
 };
 
 jfieldID otherStats_field;
+jfieldID hasSwappedOutPss_field;
 
 static bool memtrackLoaded;
 
@@ -119,6 +122,7 @@
     int privateClean;
     int sharedClean;
     int swappedOut;
+    int swappedOutPss;
 };
 
 #define BINDER_STATS "/proc/binder/stats"
@@ -206,7 +210,7 @@
     return err;
 }
 
-static void read_mapinfo(FILE *fp, stats_t* stats)
+static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
 {
     char line[1024];
     int len, nameLen;
@@ -216,7 +220,7 @@
     float sharing_proportion = 0.0;
     unsigned shared_clean = 0, shared_dirty = 0;
     unsigned private_clean = 0, private_dirty = 0;
-    unsigned swapped_out = 0;
+    unsigned swapped_out = 0, swapped_out_pss = 0;
     bool is_swappable = false;
     unsigned temp;
 
@@ -230,6 +234,8 @@
     int subHeap = HEAP_UNKNOWN;
     int prevHeap = HEAP_UNKNOWN;
 
+    *foundSwapPss = false;
+
     if(fgets(line, sizeof(line), fp) == 0) return;
 
     while (!done) {
@@ -340,6 +346,7 @@
         private_clean = 0;
         private_dirty = 0;
         swapped_out = 0;
+        swapped_out_pss = 0;
 
         while (true) {
             if (fgets(line, 1024, fp) == 0) {
@@ -365,6 +372,9 @@
                 /* referenced = temp; */
             } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
                 swapped_out = temp;
+            } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) {
+                *foundSwapPss = true;
+                swapped_out_pss = temp;
             } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
                 // looks like a new mapping
                 // example: "10000000-10001000 ---p 10000000 00:00 0"
@@ -390,6 +400,7 @@
             stats[whichHeap].privateClean += private_clean;
             stats[whichHeap].sharedClean += shared_clean;
             stats[whichHeap].swappedOut += swapped_out;
+            stats[whichHeap].swappedOutPss += swapped_out_pss;
             if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
                 stats[subHeap].pss += pss;
                 stats[subHeap].swappablePss += swappable_pss;
@@ -398,12 +409,13 @@
                 stats[subHeap].privateClean += private_clean;
                 stats[subHeap].sharedClean += shared_clean;
                 stats[subHeap].swappedOut += swapped_out;
+                stats[subHeap].swappedOutPss += swapped_out_pss;
             }
         }
     }
 }
 
-static void load_maps(int pid, stats_t* stats)
+static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
 {
     char tmp[128];
     FILE *fp;
@@ -412,17 +424,18 @@
     fp = fopen(tmp, "r");
     if (fp == 0) return;
 
-    read_mapinfo(fp, stats);
+    read_mapinfo(fp, stats, foundSwapPss);
     fclose(fp);
 }
 
 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
         jint pid, jobject object)
 {
+    bool foundSwapPss;
     stats_t stats[_NUM_HEAP];
     memset(&stats, 0, sizeof(stats));
 
-    load_maps(pid, stats);
+    load_maps(pid, stats, &foundSwapPss);
 
     struct graphics_memory_pss graphics_mem;
     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
@@ -442,6 +455,7 @@
         stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
         stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
         stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
+        stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
     }
 
     for (int i=0; i<_NUM_CORE_HEAP; i++) {
@@ -452,9 +466,11 @@
         env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
         env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
         env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
+        env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
     }
 
 
+    env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
     jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
 
     jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
@@ -471,6 +487,7 @@
         otherArray[j++] = stats[i].privateClean;
         otherArray[j++] = stats[i].sharedClean;
         otherArray[j++] = stats[i].swappedOut;
+        otherArray[j++] = stats[i].swappedOutPss;
     }
 
     env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
@@ -481,11 +498,12 @@
     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
 }
 
-static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss,
-        jlongArray outMemtrack)
+static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
+        jlongArray outUssSwapPss, jlongArray outMemtrack)
 {
     char line[1024];
     jlong pss = 0;
+    jlong swapPss = 0;
     jlong uss = 0;
     jlong memtrack = 0;
 
@@ -521,19 +539,31 @@
                     }
                     uss += atoi(c);
                 }
+            } else if (line[0] == 'S' && strncmp(line, "SwapPss:", 8) == 0) {
+                char* c = line + 8;
+                jlong lSwapPss;
+                while (*c != 0 && (*c < '0' || *c > '9')) {
+                    c++;
+                }
+                lSwapPss = atoi(c);
+                swapPss += lSwapPss;
+                pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
             }
         }
 
         fclose(fp);
     }
 
-    if (outUss != NULL) {
-        if (env->GetArrayLength(outUss) >= 1) {
-            jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
-            if (outUssArray != NULL) {
-                outUssArray[0] = uss;
+    if (outUssSwapPss != NULL) {
+        if (env->GetArrayLength(outUssSwapPss) >= 1) {
+            jlong* outUssSwapPssArray = env->GetLongArrayElements(outUssSwapPss, 0);
+            if (outUssSwapPssArray != NULL) {
+                outUssSwapPssArray[0] = uss;
+                if (env->GetArrayLength(outUssSwapPss) >= 2) {
+                    outUssSwapPssArray[1] = swapPss;
+                }
             }
-            env->ReleaseLongArrayElements(outUss, outUssArray, 0);
+            env->ReleaseLongArrayElements(outUssSwapPss, outUssSwapPssArray, 0);
         }
     }
 
@@ -1056,6 +1086,7 @@
     }
 
     otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
+    hasSwappedOutPss_field = env->GetFieldID(clazz, "hasSwappedOutPss", "Z");
 
     for (int i=0; i<_NUM_CORE_HEAP; i++) {
         stat_fields[i].pss_field =
@@ -1072,6 +1103,8 @@
                 env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
         stat_fields[i].swappedOut_field =
                 env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
+        stat_fields[i].swappedOutPss_field =
+                env->GetFieldID(clazz, stat_field_names[i].swappedOutPss_name, "I");
     }
 
     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 90606a35..3473d9d 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -578,7 +578,7 @@
     return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
 }
 
-static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
+static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
 {
     Vector<String8> locales;
 
@@ -587,7 +587,7 @@
         return NULL;
     }
 
-    am->getLocales(&locales);
+    am->getLocales(&locales, includeSystemLocales);
 
     const int N = locales.size();
 
@@ -608,6 +608,16 @@
     return result;
 }
 
+static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
+{
+    return getLocales(env, clazz, true /* include system locales */);
+}
+
+static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
+{
+    return getLocales(env, clazz, false /* don't include system locales */);
+}
+
 static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
     jobject result = env->NewObject(gConfigurationOffsets.classObject,
             gConfigurationOffsets.constructor);
@@ -2154,6 +2164,8 @@
     // Resources.
     { "getLocales",      "()[Ljava/lang/String;",
         (void*) android_content_AssetManager_getLocales },
+    { "getNonSystemLocales", "()[Ljava/lang/String;",
+        (void*) android_content_AssetManager_getNonSystemLocales },
     { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
         (void*) android_content_AssetManager_getSizeConfigurations },
     { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1090f90..b3cc234 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -103,10 +103,11 @@
 
     <protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
 
-    <protected-broadcast android:name="android.backup.intent.RUN" />
-    <protected-broadcast android:name="android.backup.intent.CLEAR" />
-    <protected-broadcast android:name="android.backup.intent.INIT" />
+    <protected-broadcast android:name="android.app.backup.intent.RUN" />
+    <protected-broadcast android:name="android.app.backup.intent.CLEAR" />
+    <protected-broadcast android:name="android.app.backup.intent.INIT" />
 
+    <protected-broadcast android:name="android.bluetooth.intent.DISCOVERABLE_TIMEOUT" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
@@ -204,6 +205,7 @@
     <protected-broadcast android:name="android.media.VOLUME_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.MASTER_VOLUME_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.MASTER_MUTE_CHANGED_ACTION" />
+    <protected-broadcast android:name="android.media.MASTER_MONO_CHANGED_ACTION" />
     <protected-broadcast android:name="android.media.SCO_AUDIO_STATE_CHANGED" />
     <protected-broadcast android:name="android.media.ACTION_SCO_AUDIO_STATE_UPDATED" />
 
@@ -218,6 +220,8 @@
     <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
     <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
 
+    <protected-broadcast android:name="android.intent.action.PICTURE_IN_PICTURE_BUTTON" />
+
     <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     <!-- @deprecated.  Only {@link android.net.ConnectivityManager.CONNECTIVITY_ACTION} is sent. -->
@@ -276,8 +280,10 @@
     <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
     <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
     <protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
-    <protected-broadcast android:name="android.intent.action.BUGREPORT_FINISHED" />
     <protected-broadcast android:name="android.intent.action.BUGREPORT_STARTED" />
+    <protected-broadcast android:name="android.intent.action.BUGREPORT_FINISHED" />
+    <protected-broadcast android:name="android.intent.action.REMOTE_BUGREPORT_FINISHED" />
+    <protected-broadcast android:name="android.intent.action.REMOTE_BUGREPORT_DISPATCH" />
 
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
@@ -381,6 +387,16 @@
     <protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
     <protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
     <protected-broadcast android:name="intent.action.ACTION_RF_BAND_INFO" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_RESOURCE_GRANTED" />
+
+    <protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED" />
+    <protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" />
+    <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+
+    <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
+    <protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -888,6 +904,11 @@
         android:protectionLevel="normal"
         android:permissionFlags="hidden"/>
 
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.FLASHLIGHT"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
     <!-- ====================================================================== -->
     <!-- INSTALL PERMISSIONS                                                    -->
     <!-- ====================================================================== -->
@@ -1052,6 +1073,12 @@
     <permission android:name="android.permission.READ_WIFI_CREDENTIAL"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi @hide Allows applications to change tether state and run
+         tether carrier provisioning.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.TETHER_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi @hide Allow system apps to receive broadcast
          when a wifi network credential is changed.
          <p>Not for use by third-party applications. -->
@@ -1193,14 +1220,6 @@
         android:description="@string/permdesc_vibrate"
         android:protectionLevel="normal" />
 
-    <!-- Allows access to the flashlight.
-         <p>Protection level: normal
-    -->
-    <permission android:name="android.permission.FLASHLIGHT"
-        android:label="@string/permlab_flashlight"
-        android:description="@string/permdesc_flashlight"
-        android:protectionLevel="normal" />
-
     <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
          from dimming.
          <p>Protection level: normal
@@ -1901,7 +1920,7 @@
 
     <!-- Allows an application to bind to third party quick settings tiles.
          <p>Should only be requested by the System, should be required by
-         QSTileService declarations.-->
+         TileService declarations.-->
     <permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
         android:protectionLevel="signature" />
 
@@ -2186,6 +2205,21 @@
     <permission android:name="android.permission.CLEAR_APP_USER_DATA"
         android:protectionLevel="signature|installer" />
 
+    <!-- @hide Allows an application to get the URI permissions
+         granted to another application.
+         <p>Not for use by third-party applications
+    -->
+    <permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows an application to clear the URI permissions
+         granted to another application.
+         <p>Not for use by third-party applications
+    -->
+    <permission
+        android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to delete cache files.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.DELETE_CACHE_FILES"
@@ -2797,6 +2831,12 @@
     <permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
             android:protectionLevel="signature" />
 
+    <!-- Allows receiving the usage of media resource e.g. video/audio codec and
+         graphic memory.
+         @hide -->
+    <permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -2946,6 +2986,18 @@
             </intent-filter>
         </activity>
 
+        <!-- Activity to prompt user if it's ok to create a new user sandbox for a
+             specified account. -->
+        <activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
+                android:excludeFromRecents="true"
+                android:process=":ui"
+                android:theme="@style/Theme.Material.DayNight.Dialog.Alert">
+            <intent-filter android:priority="1000">
+                <action android:name="android.os.action.CREATE_USER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <receiver android:name="com.android.server.BootReceiver"
                 android:systemUserOnly="true">
             <intent-filter android:priority="1000">
diff --git a/core/res/res/drawable/ic_notification_alert.xml b/core/res/res/drawable/ic_notification_alert.xml
new file mode 100644
index 0000000..d17dfc1
--- /dev/null
+++ b/core/res/res/drawable/ic_notification_alert.xml
@@ -0,0 +1,33 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M18.4,2.2L17.0,3.6c2.0,1.4 3.3,3.7 3.5,6.4l2.0,0.0C22.3,6.8 20.8,4.0 18.4,2.2z"
+        android:fillColor="#231F20"/>
+    <path
+        android:pathData="M7.1,3.6L5.7,2.2C3.3,4.0 1.7,6.8 1.5,10.0l2.0,0.0C3.7,7.3 5.0,5.0 7.1,3.6z"
+        android:fillColor="#231F20"/>
+    <path
+        android:pathData="M18.5,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.5,3.5C13.5,2.7 12.8,2.0 12.0,2.0s-1.5,0.7 -1.5,1.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.5,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.5,10.5zM13.0,16.5l-2.0,0.0l0.0,-2.0l2.0,0.0L13.0,16.5zM13.0,12.5l-2.0,0.0l0.0,-6.0l2.0,0.0L13.0,12.5z"
+        android:fillColor="#231F20"/>
+    <path
+        android:pathData="M12.0,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0L10.0,20.0C10.0,21.1 10.9,22.0 12.0,22.0z"
+        android:fillColor="#231F20"/>
+</vector>
diff --git a/core/res/res/drawable/ic_notification_block.xml b/core/res/res/drawable/ic_notification_block.xml
new file mode 100644
index 0000000..27690740
--- /dev/null
+++ b/core/res/res/drawable/ic_notification_block.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
+</vector>
diff --git a/core/res/res/drawable/seekbar_tick_mark_material.xml b/core/res/res/drawable/seekbar_tick_mark_material.xml
new file mode 100644
index 0000000..d2c38a2
--- /dev/null
+++ b/core/res/res/drawable/seekbar_tick_mark_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval"
+       android:tint="?attr/colorControlNormal">
+    <size android:width="@dimen/progress_bar_height_material"
+          android:height="@dimen/progress_bar_height_material" />
+    <solid android:color="@color/white" />
+</shape>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 04466e5..c945459 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sluit persoonlike data soos kredietkaartnommers en wagwoorde in."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Beheer vertoonskermvergroting"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Beheer die vertoonskerm se zoemvlak en posisionering."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Voer gebare uit"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan tik, swiep, knyp en ander gebare uitvoer."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"deaktiveer of verander statusbalk"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"wees die statusbalk"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Laat die program toe om foto\'s en video\'s met die kamera te neem. Hierdie toestemming laat die program toe om die kamera te eniger tyd sonder jou bevestiging te gebruik."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"beheer vibrasie"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Laat die program toe om die vibrator te beheer."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"beheer flitslig"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Laat die program toe om die flitslig te beheer."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"skakel foonnommers direk"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Laat die program toe om telefoonnommers sonder jou tussentrede te bel. Dit kan tot onverwagte heffings of oproepe lei. Let daarop dat dit nie die program toelaat om noodnommers te bel nie. Kwaadwillige programme kan jou geld kos deur oproepe sonder jou bevestiging te maak."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"toegang tot kitsboodskapoproepdiens"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b262325..17b5a29 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"እንደ የክሬዲት ካርድ ቁጥሮች እና የይለፍ ቃላት ያሉ የግል ውሂብ ያካትታል።"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"የመቆጣጠሪያ ማሳያ እንዲጎላ አደራረግ"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"የማሳያውን የማጉያ ደረጃ እና አቀማመጥ ይቆጣጠሩ።"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"የጣት ምልክቶችን ያከናውኑ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"መታ ማድረግ፣ ማንሸራተት፣ መቆንጠጥ እና ሌሎች የጣት ምልክቶችን ማከናወን ይችላል።"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"የሁኔቴ አሞሌ አቦዝን ወይም ቀይር"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"የስርዓት አዶዎችን ወደ ሁኔታ አሞሌ ላለማስቻል ወይም ለማከል እና ለማስወገድ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"የሁኔታ አሞሌ መሆን"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"መተግበሪያው በካሜራው ፎቶዎችንና ቪዲዮዎችን እንዲያነሳ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው ካሜራውን በማንኛውም ጊዜ ያላንተ ማረጋገጫ እንዲጠቀም ይፈቅድለታል።"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ነዛሪ ተቆጣጠር"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ነዛሪውን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ።"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"የብርሃንብልጭታ ተቆጣጠር"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"የብልጭታ ብርሃኑን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ።"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"በቀጥታ ስልክ ቁጥሮች ደውል"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"መተግበሪያው ያላንተ ጣልቃ ገብነት የስልክ ቁጥሮች ላይ እንዲደውል ይፈቅድለታል። ይህ ያልተጠበቁ ክፍያዎችን ወይም ጥሪዎችን ሊያስከትል ይችላል። ይህ መተግበሪያው የድንገተኛ ስልክ ቁጥሮችን እንዲደውል እንደማይፈቅድለት ልብ በል። ተንኮል አዘል መተግበሪያዎች ያላንተ ማረጋገጫ ጥሪዎችን በማድረግ ገንዘብ ሊያስወጡህ ይችላሉ።"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"የአይኤምኤስ ጥሪ አገልግሎትን ይደርሳል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 6eb005c..efde61f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -260,6 +260,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"التحكم في تكبير الشاشة"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"يمكنك التحكم في مستوى التكبير/التصغير للشاشة وتحديد الموضع."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"تنفيذ إيماءات"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"يمكن النقر والتمرير بسرعة والتصغير وتنفيذ إيماءات أخرى."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"تعطيل شريط الحالة أو تعديله"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"للسماح للتطبيق بتعطيل شريط الحالة أو إضافة رموز نظام وإزالتها."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"العمل كشريط للحالة"</string>
@@ -358,8 +360,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"للسماح للتطبيق بالتقاط صور ومقاطع فيديو من خلال الكاميرا. ويتيح هذا الإذن للتطبيق استخدام الكاميرا في أي وقت وبدون موافقة منك."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"التحكم في الاهتزاز"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"للسماح للتطبيق بالتحكم في الهزّاز."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"التحكم في الضوء الوامض"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"للسماح للتطبيق بالتحكم في الضوء الوامض."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"اتصال مباشر بأرقام الهواتف"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"للسماح للتطبيق بطلب أرقام هاتفية بدون تدخل منك. وقد يؤدي ذلك إلى تحمل رسوم غير متوقعة أو إجراء مكالمات غير متوقعة. ومن الجدير بالذكر أن ذلك لا يتيح للتطبيق الاتصال بأرقام الطوارئ. وقد تؤدي التطبيقات الضارة إلى تحملك تكاليف مالية من خلال إجراء مكالمات بدون موافقة منك."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"الوصول إلى خدمة الاتصال عبر الرسائل الفورية"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 30b6e77..0e4771e 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredit kartı nömrələri və parollar kimi şəxsi məlumatlar daxildir."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekran böyütməsinə nəzarət edin"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekran yaxınlaşdırma səviyyəsi və yerləşdirməsinə nəzarət edin."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Jestlər ilə əməliyyat aparın"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Digər jestlərə tıklaya, sürüşdürə və əməliyyat apara bilərsiniz."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"status panelini deaktivləşdir və ya dəyişdir"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Tətbiqə status panelini deaktiv etməyə və ya sistem ikonalarını əlavə etmək və ya silmək imkanı verir."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"status paneli edin"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Tətbiqə kamera ilə şəkil və video çəkməyə imkan yaradır. Bu icazə tətbiqə sizin təsdiqiniz olmadan kameradan istifadə icazəsi verir."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"vibrasiyaya nəzarət edir"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Tətbiqə vibratoru idarə etmə icazəsi verir."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"Flash işığını idarə edir"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Tətbiqə siqnal işığı na nəzarət etməyə imkan verir."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefon nömrələrinə birbaşa zəng edir"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Tətbiqə Sizin müdaxiləniz olmadan telefon zəngləri etməyə imkan verir. Zərərli tətbiqlər Sizdən xəbərsiz şəkildə müxtəlif zənglər edərək, Sizə maddi ziyan vura bilər. Qeyd: Bu, tətbiqlərə təcili nömrələrə zəng etməyə icazə vermir."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS zəng xidmətinə giriş"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0f51f0d..fd9ddec 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -257,6 +257,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obuhvata lične podatke kao što su brojevi kreditnih kartica i lozinke."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Upravljaj uvećanjem prikaza"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Upravlja nivoom zumiranja prikaza i određivanjem položaja."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Obavljanje pokreta"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Može da dodiruje, lista, skuplja prikaz i obavlja druge pokrete."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmena statusne trake"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Dozvoljava aplikaciji da onemogući statusnu traku ili da dodaje i uklanja sistemske ikone."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"funkcionisanje kao statusna traka"</string>
@@ -355,8 +357,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Dozvoljava aplikaciji da snima slike i video snimke kamerom. Ova dozvola omogućava aplikaciji da u bilo kom trenutku koristi kameru bez vaše potvrde."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrola vibracije"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Dozvoljava aplikaciji da kontroliše vibraciju."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrola osvetljenja"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Dozvoljava aplikaciji da kontroliše blic."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"direktno pozivanje brojeva telefona"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Dozvoljava aplikaciji da poziva brojeve telefona bez vaše dozvole. Ovo može da dovede do neočekivanih troškova ili poziva. Imajte na umu da ovo ne dozvoljava aplikaciji da poziva brojeve za hitne slučajeve. Zlonamerne aplikacije mogu da pozivaju bez vaše potvrde, što može da dovede do troškova."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"pristup usluzi poziva pomoću razmene trenutnih poruka"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d70f95f..955727b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включва лични данни, като например номера на кредитни карти и пароли."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управление на увеличението на дисплея"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управление на нивото на мащаба и позиционирането на дисплея."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Извършване на жестове"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Можете да докосвате, да прекарвате пръст, да събирате пръсти и да извършвате други жестове."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"деактивиране или промяна на лентата на състоянието"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Разрешава на приложението да деактивира лентата на състоянието или да добавя и премахва системни икони."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"изпълняване на ролята на лента на състоянието"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Разрешава на приложението да прави снимки и видеоклипове с камерата. Това разрешение му позволява да я използва по всяко време без потвърждение от ваша страна."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контролиране на вибрирането"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Разрешава на приложението да контролира устройството за вибрация."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"контролиране на фенерчето"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Разрешава на приложението да контролира фенерчето."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"директно обаждане до телефонни номера"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Разрешава на приложението да се обажда без ваша намеса до телефонни номера, което може да доведе до неочаквано таксуване или обаждания. Обърнете внимание, че това не му позволява да извършва обаждания до спешните служби. Злонамерените приложения могат да ви въвлекат в разходи, като извършват обаждания без потвърждение от ваша страна."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"достъп до услугата за незабавни съобщения за обаждания"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 683e2ec..6e7a77f 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্রেডিট কার্ডের নম্বর ও পাসওয়ার্ডগুলির মতো ব্যক্তিগত তথ্য অন্তর্ভুক্ত করে৷"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"প্রদর্শনের বৃহত্তরীকরণ ব্যবস্থা নিয়ন্ত্রণ করুন"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"প্রদর্শনের জুমের স্তর এবং অবস্থান নির্ধারন নিয়ন্ত্রণ করুন৷"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"অঙ্গভঙ্গির কাজগুলি সম্পাদন করুন"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"আলতো চাপ দেওয়া, সোয়াইপ, পিঞ্চ করা এবং অন্যান্য অঙ্গভঙ্গির কাজগুলি সম্পাদন করতে পারবেন৷"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"স্থিতি দন্ড নিষ্ক্রিয় অথবা সংশোধন করে"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"অ্যাপ্লিকেশানকে স্থিতি দন্ড অক্ষম করতে এবং সিস্টেম আইকনগুলি সরাতে দেয়৷"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"স্থিতি দন্ডে থাকুন"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ক্যামেরার সাহায্যে ছবি তুলতে ও ভিডিও তৈরি করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে আপনার নিশ্চয়তা ছাড়াই যেকোনো সময় ক্যামেরা ব্যবহার করতে মঞ্জুর করে৷"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"কম্পন নিয়ন্ত্রণ করুন"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"অ্যাপ্লিকেশানকে কম্পক নিয়ন্ত্রণ করতে দেয়৷"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ফ্ল্যাশলাইট নিয়ন্ত্রণ করে"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"অ্যাপ্লিকেশানকে টর্চলাইট নিয়ন্ত্রণ করতে দেয়৷"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"সরাসরি ফোন নম্বরগুলিতে কল করে"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"অ্যাপ্লিকেশানটিকে আপনার হস্তক্ষেপ ছাড়াই ফোন নম্বরগুলিতে কল করতে মঞ্জুর করে৷ এটি অপ্রত্যাশিত পরিমাণ খরচা বা কলের কারণ হতে পারে৷ মনে রাখবেন, এটি অ্যাপ্লিকেশানটির দ্বারা জরুরি নম্বরগুলিতে কল করাকে অনুমতি দেয় না৷ ক্ষতিকারক অ্যাপ্লিকেশানগুলি আপনার সম্মতি ছাড়াই কল করার ফলে আপনাকে অহেতুক অর্থ প্রদান করতে হতে পারে৷"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS পরিষেবাতে অ্যাক্সেস"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 786fea1..bbe9887 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclou dades personals com ara números de targetes de crèdit i contrasenyes."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controla l\'ampliació de la pantalla"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el nivell i el posicionament del zoom de la pantalla."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Utilitza gestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Pot tocar, lliscar, pessigar i utilitzar altres gestos."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra d\'estat"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"aparèixer a la barra d\'estat"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permet que l\'aplicació faci fotos i vídeos amb la càmera. Aquest permís permet que l\'aplicació utilitzi la càmera en qualsevol moment sense la teva confirmació."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar la vibració"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permet que l\'aplicació controli el vibrador."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controla la llanterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permet que l\'aplicació controli la llanterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"trucar directament a números de telèfon"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permet que l\'aplicació truqui a números de telèfon sense la teva intervenció. Aquesta acció pot produir càrrecs o trucades inesperades. Tingues en compte que això no permet que l\'aplicació truqui a números d\'emergència. Les aplicacions malicioses poden fer trucades sense la teva confirmació, cosa que et pot fer gastar diners."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"accés al servei de trucades IMS"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a2e12a3..f5763f9 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledování zahrnuje osobní údaje, jako jsou například čísla kreditních karet a hesla."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Nastavení zvětšení obsahu obrazovky"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Určuje umístění a úroveň přiblížení displeje."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Provádění gest"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Může provádět gesta klepnutí, přejetí, stažení prstů a další."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"zakázání či změny stavového řádku"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikaci zakázat stavový řádek nebo přidat či odebrat systémové ikony."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"vydávání se za stavový řádek"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Umožňuje aplikaci pořizovat fotografie a videa pomocí fotoaparátu. Toto oprávnění umožňuje aplikaci používat fotoaparát kdykoliv i bez vašeho svolení."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládání vibrací"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikaci ovládat vibrace."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ovládání kontrolky"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Umožňuje aplikaci ovládat svítilnu."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"přímé volání na telefonní čísla"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Umožňuje aplikaci volat na telefonní čísla bez vašeho přičinění. Může mít za následek neočekávané poplatky nebo hovory. Toto oprávnění neumožňuje aplikaci volat na tísňová čísla. Škodlivé aplikace vás mohou připravit o peníze uskutečňováním hovorů bez vašeho svolení."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"přístup ke službě zasílání rychlých zpráv pro účely hovorů"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 67b8ee9..3e8a86d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data såsom kreditkortnumre og adgangskoder."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollér skærmforstørrelsen"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollér skærmens zoomniveau og position."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Udfør bevægelser"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan trykke, stryge, knibe sammen og udføre andre bevægelser."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"deaktiver eller rediger statuslinje"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"vær statusbjælken"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Tillader, at appen kan tage billeder og videoer med kameraet. Med denne tilladelse kan appen til enhver tid bruge kameraet uden din bekræftelse."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrollere vibration"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Tillader, at appen kan kontrollere vibratoren."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontroller lommelygte"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Tillader, at appen kan kontrollere lommelygten."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ringe direkte op til telefonnumre"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Tillader, at appen kan ringe til telefonnumre uden din indgriben. Dette kan resultere i uventede opkrævninger eller opkald. Bemærk, at appen med denne tilladelse ikke kan ringe til nødopkaldsnumre. Skadelige apps kan koste dig penge ved at foretage opkald uden din bekræftelse."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"få adgang til chat-opkaldstjeneste"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2d64aee..e2fb6ff 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -56,10 +56,10 @@
     <string name="badPin" msgid="9015277645546710014">"Die von Ihnen eingegebene alte PIN ist nicht korrekt."</string>
     <string name="badPuk" msgid="5487257647081132201">"Der von Ihnen eingegebene PUK ist nicht korrekt."</string>
     <string name="mismatchPin" msgid="609379054496863419">"Die von Ihnen eingegebenen PIN-Nummern stimmen nicht überein."</string>
-    <string name="invalidPin" msgid="3850018445187475377">"Geben Sie eine PIN ein, die 4 bis 8 Zahlen enthält."</string>
-    <string name="invalidPuk" msgid="8761456210898036513">"Geben Sie eine mindestens achtstellige PUK ein."</string>
-    <string name="needPuk" msgid="919668385956251611">"Ihre SIM-Karte ist mit einem PUK gesperrt. Geben Sie zum Entsperren den PUK-Code ein."</string>
-    <string name="needPuk2" msgid="4526033371987193070">"Geben Sie zum Entsperren der SIM-Karte den PUK2 ein."</string>
+    <string name="invalidPin" msgid="3850018445187475377">"Gib eine PIN ein, die 4 bis 8 Zahlen enthält."</string>
+    <string name="invalidPuk" msgid="8761456210898036513">"Gib eine mindestens achtstellige PUK ein."</string>
+    <string name="needPuk" msgid="919668385956251611">"Deine SIM-Karte ist mit einem PUK gesperrt. Gib zum Entsperren den PUK-Code ein."</string>
+    <string name="needPuk2" msgid="4526033371987193070">"Gib zum Entsperren der SIM-Karte den PUK2 ein."</string>
     <string name="enablePin" msgid="209412020907207950">"Fehler. SIM-/RUIM-Sperre aktivieren."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
       <item quantity="other">Sie haben noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche, bevor Ihre SIM-Karte gesperrt wird.</item>
@@ -87,7 +87,7 @@
     <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Beschränkt"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Nicht beschränkt"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Dienst nicht eingerichtet."</string>
-    <string name="CLIRPermanent" msgid="3377371145926835671">"Sie können die Einstellung für die Anrufer-ID nicht ändern."</string>
+    <string name="CLIRPermanent" msgid="3377371145926835671">"Du kannst die Einstellung für die Anrufer-ID nicht ändern."</string>
     <string name="RestrictedChangedTitle" msgid="5592189398956187498">"Eingeschränkter Zugriff geändert"</string>
     <string name="RestrictedOnData" msgid="8653794784690065540">"Daten-Dienst ist gesperrt."</string>
     <string name="RestrictedOnEmergency" msgid="6581163779072833665">"Notruf ist gesperrt."</string>
@@ -156,7 +156,7 @@
     <string name="httpErrorBadUrl" msgid="3636929722728881972">"Die Seite kann nicht geöffnet werden, weil die URL ungültig ist."</string>
     <string name="httpErrorFile" msgid="2170788515052558676">"Auf die Datei konnte nicht zugegriffen werden."</string>
     <string name="httpErrorFileNotFound" msgid="6203856612042655084">"Die angeforderte Datei wurde nicht gefunden."</string>
-    <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Versuchen Sie es später erneut."</string>
+    <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Versuche es später erneut."</string>
     <string name="notification_title" msgid="8967710025036163822">"Fehler bei Anmeldung für <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
     <string name="contentServiceSync" msgid="8353523060269335667">"Synchronisierung"</string>
     <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronisierung"</string>
@@ -198,7 +198,7 @@
     <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Ihr Fernseher wird ausgeschaltet."</string>
     <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Ihre Uhr wird heruntergefahren."</string>
     <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefon wird heruntergefahren."</string>
-    <string name="shutdown_confirm_question" msgid="2906544768881136183">"Möchten Sie das Gerät herunterfahren?"</string>
+    <string name="shutdown_confirm_question" msgid="2906544768881136183">"Möchtest du das Gerät herunterfahren?"</string>
     <string name="reboot_safemode_title" msgid="7054509914500140361">"Im abgesicherten Modus starten"</string>
     <string name="reboot_safemode_confirm" msgid="55293944502784668">"Möchten Sie im abgesicherten Modus neu starten? Dadurch werden alle Apps von Drittanbietern deaktiviert, die Sie installiert haben. Sie werden jedoch nach einem weiteren Neustart wiederhergestellt."</string>
     <string name="recent_tasks_title" msgid="3691764623638127888">"Kürzlich geöffnet"</string>
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displayvergrößerung festlegen"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe des Displays und die Zoom-Position auf dem Display fest."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Bewegungen möglich"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Tippen, Wischen, Zusammenziehen und andere Bewegungen möglich."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"Statusleiste deaktivieren oder ändern"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"Statusleiste darstellen"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ermöglicht der App, Bilder und Videos mit der Kamera aufzunehmen. Die Berechtigung erlaubt der App, die Kamera jederzeit und ohne Ihre Bestätigung zu nutzen."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"Vibrationsalarm steuern"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Ermöglicht der App, den Vibrationsalarm zu steuern"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"Lichtanzeige steuern"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Ermöglicht der App, die Lichtanzeige zu steuern"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"Telefonnummern direkt anrufen"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Ermöglicht der App, ohne Ihr Eingreifen Telefonnummern zu wählen. Dies kann zu unerwarteten Kosten und Anrufen führen. Beachten Sie, dass die App keine Notrufnummern wählen kann. Schädliche Apps verursachen möglicherweise Kosten, indem sie Anrufe ohne Ihre Bestätigung tätigen."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"Zugriff auf IMS-Anrufdienst"</string>
@@ -652,11 +652,11 @@
     <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Passwort zum Entsperren eingeben"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"PIN zum Entsperren eingeben"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Falscher PIN-Code"</string>
-    <string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
+    <string name="keyguard_label_text" msgid="861796461028298424">"Drücke zum Entsperren die Menütaste und dann auf \"0\"."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Notrufnummer"</string>
     <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Kein Dienst"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt"</string>
-    <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
+    <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücke die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
     <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Muster zum Entsperren zeichnen"</string>
     <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Notfall"</string>
@@ -669,10 +669,10 @@
     <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Keine SIM-Karte im Tablet"</string>
     <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Keine SIM-Karte im Fernseher"</string>
     <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Keine SIM-Karte im Telefon"</string>
-    <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Legen Sie eine SIM-Karte ein."</string>
-    <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-Karte fehlt oder ist nicht lesbar. Bitte legen Sie eine SIM-Karte ein."</string>
+    <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Lege eine SIM-Karte ein."</string>
+    <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-Karte fehlt oder ist nicht lesbar. Bitte lege eine SIM-Karte ein."</string>
     <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"SIM-Karte unbrauchbar"</string>
-    <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Ihre SIM-Karte wurde dauerhaft deaktiviert.\n Wenden Sie sich an Ihren Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string>
+    <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Deine SIM-Karte wurde dauerhaft deaktiviert.\n Wende dich an deinen Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string>
     <string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Vorheriger Titel"</string>
     <string name="lockscreen_transport_next_description" msgid="573285210424377338">"Nächster Titel"</string>
     <string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Pausieren"</string>
@@ -683,22 +683,22 @@
     <string name="emergency_calls_only" msgid="6733978304386365407">"Nur Notrufe"</string>
     <string name="lockscreen_network_locked_message" msgid="143389224986028501">"Netzwerk gesperrt"</string>
     <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"PUK-Sperre auf SIM"</string>
-    <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Weitere Informationen erhalten Sie im Nutzerhandbuch oder beim Kundendienst."</string>
+    <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Weitere Informationen erhältst du im Nutzerhandbuch oder beim Kundendienst."</string>
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"PIN eingeben"</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-Karte wird entsperrt..."</string>
-    <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
-    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
-    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Bitte versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
+    <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du hast dein Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du hast dein Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wirst du aufgefordert, dein Tablet mithilfe deiner Google-Anmeldeinformationen zu entsperren.\n\n Bitte versuche es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Wenn Sie es noch <xliff:g id="NUMBER_1">%2$d</xliff:g>-mal falsch eingeben, werden Sie aufgefordert, Ihren Fernseher mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
-    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wirst du aufgefordert, dein Telefon mithilfe deiner Google-Anmeldeinformationen zu entsperren.\n\nBitte versuche es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
+    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du hast <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird der Fernseher auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
-    <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+    <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du hast <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du hast <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Der Fernseher wird nun auf die Werkseinstellungen zurückgesetzt."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
-    <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du hast <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+    <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Versuche es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Muster vergessen?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Kontoentsperrung"</string>
     <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Zu viele Schemaversuche"</string>
@@ -788,11 +788,11 @@
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ermöglicht der App, Nachrichten zu Ihrem Mailbox-Posteingang hinzuzufügen"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ermöglicht der App, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Apps können so Standortinformationen an beliebige Websites senden."</string>
-    <string name="save_password_message" msgid="767344687139195790">"Möchten Sie, dass der Browser dieses Passwort speichert?"</string>
+    <string name="save_password_message" msgid="767344687139195790">"Möchtest du, dass der Browser dieses Passwort speichert?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Nicht jetzt"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Speichern"</string>
     <string name="save_password_never" msgid="8274330296785855105">"Nie"</string>
-    <string name="open_permission_deny" msgid="7374036708316629800">"Sie sind nicht zum Öffnen dieser Seite berechtigt."</string>
+    <string name="open_permission_deny" msgid="7374036708316629800">"Du bist nicht zum Öffnen dieser Seite berechtigt."</string>
     <string name="text_copied" msgid="4985729524670131385">"Text in Zwischenablage kopiert."</string>
     <string name="more_item_label" msgid="4650918923083320495">"Mehr"</string>
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"Menü+"</string>
@@ -905,10 +905,10 @@
     <string name="aerr_process" msgid="4507058997035697579">"Der Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" wurde beendet."</string>
     <string name="aerr_process_silence" msgid="4226685530196000222">"Silence stürzt bei <xliff:g id="PROCESS">%1$s</xliff:g> bis zum Neustart ab."</string>
     <string name="anr_title" msgid="4351948481459135709"></string>
-    <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> reagiert nicht.\n\nMöchten Sie die App schließen?"</string>
-    <string name="anr_activity_process" msgid="5776209883299089767">"Aktivität \"<xliff:g id="ACTIVITY">%1$s</xliff:g>\" reagiert nicht.\n\nMöchten Sie sie beenden?"</string>
-    <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> reagiert nicht. Möchten Sie die App schließen?"</string>
-    <string name="anr_process" msgid="6513209874880517125">"Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" reagiert nicht.\n\nMöchten Sie ihn beenden?"</string>
+    <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> reagiert nicht.\n\nMöchtest du die App schließen?"</string>
+    <string name="anr_activity_process" msgid="5776209883299089767">"Aktivität \"<xliff:g id="ACTIVITY">%1$s</xliff:g>\" reagiert nicht.\n\nMöchtest du sie beenden?"</string>
+    <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> reagiert nicht. Möchtest du die App schließen?"</string>
+    <string name="anr_process" msgid="6513209874880517125">"Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" reagiert nicht.\n\nMöchtest du ihn beenden?"</string>
     <string name="force_close" msgid="8346072094521265605">"OK"</string>
     <string name="report" msgid="4060218260984795706">"Bericht"</string>
     <string name="wait" msgid="7147118217226317732">"Warten"</string>
@@ -1096,7 +1096,7 @@
     <string name="dial_number_using" msgid="5789176425167573586">"Nummer\nmit <xliff:g id="NUMBER">%s</xliff:g> wählen"</string>
     <string name="create_contact_using" msgid="4947405226788104538">"Neuer Kontakt\nmit <xliff:g id="NUMBER">%s</xliff:g> erstellen"</string>
     <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"Die folgenden Apps benötigen die Berechtigung zum aktuellen und zukünftigen Zugriff auf Ihr Konto."</string>
-    <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Möchten Sie diese Anfrage zulassen?"</string>
+    <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Möchtest du diese Anfrage zulassen?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"Zugriffsanforderung"</string>
     <string name="allow" msgid="7225948811296386551">"Zulassen"</string>
     <string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index a1a6ced..3e277c3 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Περιλαμβάνει προσωπικά δεδομένα, όπως είναι οι αριθμοί πιστωτικών καρτών και οι κωδικοί πρόσβασης."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ελέγξτε τη μεγέθυνση της οθόνης"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ελέγξτε το επίπεδο ζουμ και τη θέση της οθόνης."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Εκτέλεση κινήσεων"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Επιτρέπει το πάτημα, την ολίσθηση, το πλησίασμα και άλλες κινήσεις."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποίηση ή τροποποίηση γραμμής κατάστασης"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ορισμός ως γραμμής κατάστασης"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Επιτρέπει στην εφαρμογή τη λήψη φωτογραφιών και βίντεο με τη φωτογραφική μηχανή. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να χρησιμοποιεί τη φωτογραφική μηχανή ανά πάσα στιγμή χωρίς την έγκρισή σας."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"έλεγχος δόνησης"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Επιτρέπει στην εφαρμογή τον έλεγχο της δόνησης."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"έλεγχος φακού"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Επιτρέπει στην εφαρμογή τον έλεγχο του φακού."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"απευθείας κλήση τηλεφωνικών αριθμών"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Επιτρέπει στην εφαρμογή την κλήση αριθμών τηλεφώνου χωρίς δική σας παρέμβαση. Αυτό μπορεί να προκαλέσει μη αναμενόμενες χρεώσεις ή κλήσεις. Έχετε υπόψη ότι δεν επιτρέπεται στην εφαρμογή η κλήση αριθμών έκτακτης ανάγκης. Οι κακόβουλες εφαρμογές ενδέχεται να σας κοστίσουν χρήματα, πραγματοποιώντας κλήσεις χωρίς την έγκρισή σας."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"πρόσβαση στην υπηρεσία κλήσεων της IMS"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index cd2dc23..0edc438 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Perform gestures"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Can tap, swipe, pinch and perform other gestures."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"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="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Allows the app to control the vibrator."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"control flashlight"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Allows the app to control the flashlight."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"directly call phone numbers"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Allows the app to call phone numbers without your intervention. This may result in unexpected charges or calls. Note that this doesn\'t allow the app to call emergency numbers. Malicious apps may cost you money by making calls without your confirmation."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"access IMS call service"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index cd2dc23..0edc438 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Perform gestures"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Can tap, swipe, pinch and perform other gestures."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"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="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Allows the app to control the vibrator."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"control flashlight"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Allows the app to control the flashlight."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"directly call phone numbers"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Allows the app to call phone numbers without your intervention. This may result in unexpected charges or calls. Note that this doesn\'t allow the app to call emergency numbers. Malicious apps may cost you money by making calls without your confirmation."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"access IMS call service"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index cd2dc23..0edc438 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Perform gestures"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Can tap, swipe, pinch and perform other gestures."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"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="permlab_vibrate" msgid="7696427026057705834">"control vibration"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Allows the app to control the vibrator."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"control flashlight"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Allows the app to control the flashlight."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"directly call phone numbers"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Allows the app to call phone numbers without your intervention. This may result in unexpected charges or calls. Note that this doesn\'t allow the app to call emergency numbers. Malicious apps may cost you money by making calls without your confirmation."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"access IMS call service"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a6f76ac..bd2f91b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar la ampliación de pantalla"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Usar gestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Permite presionar, deslizar, pellizcar y usar otros gestos."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra de estado"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que la aplicación saque fotos o grabe videos con la cámara. Este permiso autoriza a la aplicación a utilizar la cámara en cualquier momento sin tu confirmación."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar la vibración"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite que la aplicación controle la vibración."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controlar linterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite que la aplicación controle la función de linterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"llamar directamente a números de teléfono"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite que la aplicación haga llamadas a números de teléfono sin intervención del usuario, lo que puede dar lugar a llamadas o cargos inesperados. Ten en cuenta que las aplicaciones no pueden usar este servicio para realizar llamadas a números de emergencia, pero las aplicaciones malintencionadas pueden causarte gastos imprevistos al realizar llamadas sin tu confirmación."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"acceder al servicio IMS para realizar llamadas"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 594fa50..e73b425 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales como números de tarjetas de crédito y contraseñas."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controla la ampliación de la pantalla"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Realizar gestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Puedes tocar y pellizcar la pantalla, deslizar el dedo y hacer otros gestos."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"inhabilitar o modificar la barra de estado"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que la aplicación haga fotos o grabe vídeos con la cámara. Este permiso autoriza a la aplicación a utilizar la cámara en cualquier momento sin tu confirmación."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar la vibración"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite que la aplicación controle la función de vibración."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controlar linterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite que la aplicación controle la función de linterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"llamar directamente a números de teléfono"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite que la aplicación haga llamadas sin intervención del usuario, lo que puede dar lugar a llamadas o cargos inesperados. Ten en cuenta que las aplicaciones no pueden usar este servicio para realizar llamadas a números de emergencia, pero las aplicaciones malintencionadas pueden causarte gastos imprevistos al realizar llamadas sin tu confirmación."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"acceder al servicio de llamadas IMS"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 8ee8904..e6a122c 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisaldab isiklikke andmeid, nt krediitkaardi numbreid ja paroole."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekraani suurenduse juhtimine"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Saate juhtida ekraani suumitaset ja asendit."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Liigutuste tegemine"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Saate puudutada, pühkida, sõrmi kokku-lahku liigutada ja teisi liigutusi teha."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"keela või muuda olekuriba"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"olekuribana kuvamine"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Võimaldab rakendusel teha kaameraga pilte ja videoid. See luba võimaldab rakendusel kasutada kaamerat mis tahes ajal teie kinnituseta."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"juhtige vibreerimist"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Võimaldab rakendusel juhtida vibreerimist."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"juhi taskulampi"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Võimaldab rakendusel juhtida taskulampi."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"helista otse telefoninumbritele"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Võimaldab rakendusel teie sekkumiseta telefoninumbritele helistada. See võib põhjustada ootamatuid tasusid või telefonikõnesid. Pange tähele, et see ei luba rakendusel helistada hädaabinumbritele. Pahatahtlikud rakendused võivad teile kulusid tekitada, tehes telefonikõnesid teie kinnituseta."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"juurdepääs IMS-kõneteenusele"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index e257fe9..31a073b 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ez da salbuespenik egiten datu pertsonalekin, hala nola, kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolatu pantailaren zoom-maila"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolatu pantailaren zoom-maila eta kokapena."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Keinuak egin"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Sakatu, lerratu, atximurkatu eta beste hainbat keinu egin ditzake."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"Desgaitu edo aldatu egoera-barra"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"Bihurtu egoera-barra"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Kamerarekin argazkiak ateratzeko eta bideoak grabatzeko baimena ematen die aplikazioei. Baimen horrekin, aplikazioak kamera edonoiz erabil dezake zure baimenik gabe."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"bibrazioa kontrolatzea"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Bibragailua kontrolatzea baimentzen die aplikazioei."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrolatu linterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Linterna kontrolatzea baimentzen die aplikazioei."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"deitu zuzenean telefono-zenbakietara"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Telefono-zenbakietara zuk esku hartu gabe deitzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak edo deiak eragin daitezke. Aplikazio gaiztoek erabil dezakete zuk berretsi gabeko deiak eginda gastuak eragiteko."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"Atzitu IMS dei-zerbitzua"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b6b669e..f0271d6 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اطلاعات شخصی مانند شماره کارت اعتباری و گذرواژه‌ها را لحاظ می‌کند."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"کنترل درشت‌نمایی نمایشگر"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"سطح و موقعیت بزرگ‌نمایی نمایشگر را کنترل کنید."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"اجرای اشاره‌ها"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"می‌توانید ضربه بزنید، انگشتتان را تند بکشید، انگشتانتان را به هم نزدیک یا از هم دور کنید و اشاره‌های دیگری اجرا کنید."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"غیرفعال کردن یا تغییر نوار وضعیت"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"‏به برنامه اجازه می‎دهد تا نوار وضعیت را غیرفعال کند یا نمادهای سیستم را اضافه یا حذف کند."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"نوار وضعیت باشد"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"به برنامه اجازه می‌دهد با دوربین به عکسبرداری و فیلمبرداری بپردازد. این مجوز به برنامه اجازه می‌‌دهد از دوربین در هر زمانی بدون تأیید شما استفاده کند."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"کنترل لرزش"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"‏به برنامه اجازه می‎دهد تا لرزاننده را کنترل کند."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"کنترل چراغ قوه"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"‏به برنامه اجازه می‎دهد تا چراغ قوه را کنترل کند."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"تماس مستقیم با شماره تلفن‌ها"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"به برنامه اجازه می‌دهد بدون دخالت شما با شماره‌های تلفن تماس بگیرد. این ممکن است باعث ایجاد هزینه یا تماس‌های پیش‌بینی نشده شود. توجه داشته باشید که این به برنامه اجازه نمی‌دهد به برقراری تماس‌های اضطراری بپردازد. برنامه‌های مخرب ممکن است با برقراری تماس بدون تأیید شما هزینه‌هایی را برای شما ایجاد کنند."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"‏دسترسی به سرویس تماس IMS"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index b0efd96..3e0c172 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisältää henkilökohtaisia tietoja, kuten luottokortin numeroita ja salasanoja."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Näytön suurentamisen hallinnointi"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Hallinnoi näytön zoomaustasoa ja asettelua."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Eleiden käyttäminen"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Lupa napauttaa, pyyhkäistä, nipistää ja käyttää muita eleitä."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"poista tilapalkki käytöstä tai muokkaa tilapalkkia"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"sijaita tilapalkissa"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Antaa sovelluksen ottaa kuvia ja kuvata videoita kameralla. Sovellus voi käyttää kameraa milloin tahansa ilman lupaasi."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"hallitse värinää"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Antaa sovelluksen hallita värinää."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"hallitse taskulamppua"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Antaa sovelluksen hallita taskulamppua."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"soittaa puhelinnumeroihin suoraan"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Antaa sovelluksen soittaa puhelinnumeroihin kysymättä sinulta. Tämä voi aiheuttaa odottamattomia kuluja tai puheluita. Huomaa, että tämä ei anna sovellukselle lupaa soittaa hätänumeroihin. Haitalliset sovellukset voivat aiheuttaa sinulle kuluja soittamalla puheluita ilman lupaa."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"pikaviestipalvelun puhelukäyttöoikeus"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 52a47587..8450e5c 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Contrôler l\'agrandissement de l\'écran"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Contrôler le niveau de zoom et le positionnement de l\'écran."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Effectuer des gestes"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Peut toucher, balayer, pincer et effectuer d\'autres gestes."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"désactiver ou modifier la barre d\'état"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"servir de barre d\'état"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permet à l\'application de prendre des photos et de filmer des vidéos avec l\'appareil photo. Cette autorisation lui permet d\'utiliser l\'appareil photo à tout moment sans votre consentement."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"gérer le vibreur"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permet à l\'application de gérer le vibreur de l\'appareil."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"gérer la lampe de poche"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permet à l\'application de gérer la lampe de poche."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"appeler directement des numéros de téléphone"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permet à l\'application d\'appeler des numéros de téléphone sans votre intervention. Cette autorisation peut entraîner des frais ou des appels imprévus et ne permet pas à l\'application d\'appeler des numéros d\'urgence. Des applications malveillantes peuvent générer des frais en passant des appels sans votre consentement."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"accéder au service d\'appel IMS"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 2740f3d..84c43fac 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Contrôler l\'agrandissement de l\'écran"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Contrôler le niveau de zoom et le positionnement de l\'écran"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Effectuer des gestes"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Permet d\'appuyer sur l\'écran, de le balayer, de le pincer et d\'effectuer d\'autres gestes."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"Désactivation ou modification de la barre d\'état"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"remplacer la barre d\'état"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permet à l\'application de prendre des photos et de filmer des vidéos avec l\'appareil photo. Cette autorisation lui permet d\'utiliser l\'appareil photo à tout moment sans votre consentement."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"contrôler le vibreur"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permet à l\'application de contrôler le vibreur."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"contrôler la lampe de poche"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permet à l\'application de contrôler la lampe de poche."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"appeler directement les numéros de téléphone"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permet à l\'application d\'appeler des numéros de téléphone sans votre intervention. Cette autorisation peut entraîner des frais ou des appels imprévus et ne permet pas à l\'application d\'appeler des numéros d\'urgence. Les applications malveillantes peuvent générer des frais en passant des appels sans votre consentement."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"accéder au service d\'appel IMS"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 71ce6e9b..36594e0 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclúe datos persoais como números e contrasinais de tarxetas de crédito."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliación da pantalla"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o nivel do zoom e o posicionamento da pantalla"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Realizar xestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Podes tocar, pasar o dedo, beliscar e realizar outros xestos."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar a barra de estado"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite á aplicación desactivar a barra de estado ou engadir e eliminar as iconas do sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"actuar como a barra de estado"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite á aplicación tomar imaxes e vídeos coa cámara. Con este permiso a aplicación pode utilizar a cámara en calquera momento sen a túa confirmación."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar a vibración"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite á aplicación controlar o vibrador."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controlar a lanterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite á aplicación controlar a luz do flash."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"chamar directamente aos números de teléfono"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite á aplicación chamar a números de teléfono sen a túa intervención. Esta acción pode implicar chamadas ou custos inesperados. Ten en conta que isto non permite á aplicación chamar a números de emerxencia. É posible que aplicacións maliciosas che custen diñeiro debido á realización de chamadas sen a túa confirmación."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"acceso ao servizo de chamadas de IMS"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 8258dc2..9d098fc 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર્સ અને પાસવર્ડ્સ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"પ્રદર્શન વિસ્તૃતિકરણ નિયંત્રિત કરો"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"પ્રદર્શનનું ઝૂમ સ્તર અને સ્થિતિનિર્ધારણ નિયંત્રિત કરો."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"હાવભાવ કરો"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ટૅપ, સ્વાઇપ, પિંચ કરી અને અન્ય હાવભાવ કરી શકે છે."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"સ્થિતિ બાર અક્ષમ કરો અથવા સંશોધિત કરો"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"એપ્લિકેશનને સ્થિતિ બાર અક્ષમ કરવાની અથવા સિસ્ટમ આયકન્સ ઉમેરવા અને દૂર કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"સ્થિતિ બાર થાઓ"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"એપ્લિકેશનને કૅમેરા વડે ચિત્રો અને વિડિઓઝ લેવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને તમારી પુષ્ટિ વિના કોઈપણ સમયે કૅમેરાના ઉપયોગની મંજૂરી આપે છે."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"વાઇબ્રેશન નિયંત્રિત કરો"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"એપ્લિકેશનને વાઇબ્રેટરને નિયંત્રિત કરવાની મંજૂરી આપે છે."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ફ્લેશલાઇટ નિયંત્રિત કરો"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"એપ્લિકેશનને ફ્લેશલાઇટને નિયંત્રિત કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"સીધા જ ફોન નંબર્સ પર કૉલ કરો"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"એપ્લિકેશનને તમારા હસ્તક્ષેપ વિના ફોન નંબર્સ પર કૉલ કરવાની મંજૂરી આપે છે. આ અનપેક્ષિત શુલ્ક અથવા કૉલ્સમાં પરિણમી શકે છે. નોંધો કે આ એપ્લિકેશનને કટોકટીના નંબર્સ પર કૉલ કરવાની મંજૂરી આપતું નથી. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો તમારી પુષ્ટિ વિના કૉલ્સ કરીને તમારા પૈસા ખર્ચ કરી શકે છે."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS કૉલ સેવા ઍક્સેસ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3f374ac6..5741d5c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर और पासवर्ड जैसा व्यक्तिगत डेटा शामिल होता है."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन आवर्धन नियंत्रित करें"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शन का ज़ूम स्‍तर और स्‍थिति निर्धारण नियंत्रित करें."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"हावभाव निष्पादित करें"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"इस सेवा के द्वारा टैप किया जा सकता है, स्वाइप किया जा सकता है, पिंच किया जा सकता है और अन्य हावभाव निष्पादित किए जा सकते हैं."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"स्‍थिति बार अक्षम या बदलें"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ऐप्स  को स्थिति बार अक्षम करने या सिस्‍टम आइकन को जोड़ने या निकालने देता है."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"स्‍थिति बार होने दें"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ऐप्स  को कैमरे से चित्र और वीडियो लेने देता है. यह अनुमति ऐप्स  को किसी भी समय आपकी पुष्टि के बिना कैमरे का उपयोग करने देती है."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"कंपन नियंत्रित करें"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ऐप्स को कंपनकर्ता नियंत्रित करने देता है."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"फ़्लैशलाइट नियंत्रित करें"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ऐप्स को फ़्लैशलाइट नियंत्रित करने देता है."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"फ़ोन नंबर पर सीधे कॉल करें"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ऐप्स  को आपके हस्‍तक्षेप के बिना फ़ोन नंबर पर कॉल करने देता है. इसके परिणाम अप्रत्‍याशित शुल्‍क या कॉल हो सकते हैं. ध्यान दें कि यह ऐप्स  को आपातकालीन नंबर पर कॉल नहीं करने देता. दुर्भावनापूर्ण ऐप्स  आपकी पुष्टि के बिना कॉल करके आपका धन व्‍यय कर सकते हैं."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS कॉल सेवा ऐक्‍सेस करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index bf89366..f635b15 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -257,6 +257,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Uključuje osobne podatke kao što su brojevi kreditnih kartica i zaporke."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrola uvećanja zaslona"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolira razinu zumiranja i položaj zaslona."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Izvođenje pokreta"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Može dodirnuti, prijeći prstom, spojiti prste i izvoditi druge pokrete."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmjena trake statusa"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"biti traka statusa"</string>
@@ -355,8 +357,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Aplikaciji omogućuje snimanje slika i videozapisa fotoaparatom. Ta dozvola aplikaciji omogućuje upotrebu fotoaparata u bilo kojem trenutku bez vašeg odobrenja."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"upravljanje vibracijom"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Aplikaciji omogućuje nadzor nad vibratorom."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"upravljanje svjetiljkom"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Aplikaciji omogućuje upravljanje svjetiljkom."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"izravno pozivanje telefonskog broja"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Aplikaciji omogućuje pozivanje telefonskih brojeva bez vašeg sudjelovanja. To može dovesti do neočekivanih troškova ili poziva. Uzmite u obzir da se aplikaciji time ne omogućuje pozivanje brojeva u nuždi. Zlonamjerne aplikacije mogu vam uzrokovati dodatne troškove postavljanjem poziva bez vašeg odobrenja."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"pristupiti usluzi poziva izravnih poruka"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 64e2229..64a674c 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Beleértve a személyes adatokat, például a hitelkártyaszámokat és jelszavakat."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"A kijelző nagyításának vezérlése"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"A kijelző nagyítási/kicsinyítési szintjének és pozíciójának vezérlése"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Kézmozdulatok végrehajtása"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Koppintás, ujjak gyors csúsztatása és összehúzása, illetve egyéb kézmozdulatok végrehajtása."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"állapotsor kikapcsolása vagy módosítása"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Lehetővé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"az állapotsor szerepének átvétele"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Lehetővé teszi az alkalmazás számára, hogy a fényképezőgéppel fotókat és videókat készítsen. Az engedéllyel rendelkező alkalmazás bármikor, az Ön jóváhagyása nélkül használhatja a fényképezőgépet."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"rezgés szabályozása"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Lehetővé teszi az alkalmazás számára a rezgés vezérlését."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"vaku vezérlése"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Lehetővé teszi az alkalmazás számára a vaku vezérlését."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefonszámok közvetlen hívása"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Lehetővé teszi az alkalmazás számára, hogy az Ön jóváhagyása nélkül hívjon fel telefonszámokat. Ennek eredményeként váratlan terhelésekkel vagy telefonhívásokkal találkozhat. Vegye figyelembe, hogy ez nem teszi lehetővé segélyhívó számok hívását az alkalmazás számára. A rosszindulatú alkalmazások az Ön jóváhagyása nélkül kezdeményezhetnek hívásokat, így költségek merülhetnek fel."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"hozzáférés az IMS-hívásszolgáltatáshoz"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 6fdccd3..8d6272a 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ներառում է անձնական տվյալներ, ինչպիսիք են վարկային քարտերի համարները և գաղտնաբառերը:"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ցուցասարքի խոշորացման կառավարում"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ցուցասարքի մասշտաբավորման և դիրքավորման կառավարում:"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Կատարել ժեստեր"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Կարող է հպել, թերթել, պտղունցել և կատարել այլ ժեստեր:"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"անջատել կամ փոփոխել կարգավիճակի գոտին"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Թույլ է տալիս հավելվածին անջատել կարգավիճակի գոտին կամ ավելացնել ու հեռացնել համակարգի պատկերակները:"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"լինել կարգավիճակի գոտի"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Թույլ է տալիս հավելվածին ֆոտոխցիկով լուսանկարել և տեսանկարել: Այս թույլտվությունը հնարավորություն է տալիս հավելվածին օգտագործել ֆոտոխցիկը ցանկացած ժամանակ` առանց ձեր հաստատման:"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"կառավարել թրթռումը"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Թույլ է տալիս հավելվածին կառավարել թրթռոցը:"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"կառավարել լապտերը"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Թույլ է տալիս հավելվածին կառավարել լապտերը:"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ուղղակիորեն զանգել հեռախոսահամարներին"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Թույլ է տալիս հավելվածին զանգել հեռախոսահամարներին առանց ձեր միջամտության: Սա կարող է հանգեցնել անկանխատեսելի գանձումների կամ զանգերի: Նկատի ունեցեք, որ սա թույլ չի տալիս հավելվածին զանգել արտակարգ իրավիճակների համարներին: Վնասարար հավելվածները կարող են ձեր հաշվից զանգեր կատարել` առանց ձեր հաստատման:"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"օգտվել IMS զանգերի ծառայությունից"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 4960267..898f6d1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Mengontrol perbesaran layar"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Mengontrol tingkat zoom dan pemosisian layar."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Melakukan isyarat"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Dapat mengetuk, menggesek, mencubit, dan melakukan isyarat lainnya."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"nonaktifkan atau ubah bilah status"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"jadikan bilah status"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Memungkinkan aplikasi mengambil gambar dan video dengan kamera. Izin ini memungkinkan aplikasi menggunakan kamera kapan saja tanpa konfirmasi Anda."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrol getaran"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Mengizinkan aplikasi untuk mengendalikan vibrator."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"mengontrol lampu senter"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Mengizinkan apl mengontrol lampu kilat."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"panggil nomor telepon secara langsung"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Memungkinkan aplikasi menghubungi nomor telepon tanpa campur tangan Anda. Izin ini dapat mengakibatkan biaya atau panggilan tak terduga. Perhatikan bahwa izin ini tidak memungkinkan aplikasi menghubungi nomor darurat. Aplikasi berbahaya dapat menyebabkan Anda dikenakan biaya dengan melakukan panggilan tanpa konfirmasi Anda."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"akses layanan panggilan IMS"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 35ae53d..ebcde5d 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Felur í sér persónuleg gögn á borð við kreditkortanúmer og aðgangsorð."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Stilla skjástærð"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Stjórnaðu aðdrætti og afstöðu skjásins."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Nota bendingar"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Getur ýtt, strokið, fært fingur saman og gert ýmsar aðrar bendingar."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"slökkva á eða breyta stöðustiku"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Leyfir forriti að slökkva á stöðustikunni eða bæta við og fjarlægja kerfistákn."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"vera stöðustikan"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Leyfir forriti að taka myndir og myndskeið með myndavélinni. Þessi heimild leyfir forritinu að nota myndavélina hvenær sem er án þinnar heimildar."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"stjórna titringi"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Leyfir forriti að stjórna titraranum."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"stjórna vasaljósi"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Leyfir forriti að stjórna vasaljósinu."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"hringja beint í símanúmer"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Leyfir forriti að hringja í símanúmer án íhlutunar notanda. Þetta getur haft í för með sér óumbeðin gjöld og símtöl. Athugaðu að þetta leyfir forritinu ekki að hringja í neyðarnúmer. Spilliforrit geta stofnað til kostnaðar fyrir þig með því að hringja símtöl án þinnar heimildar."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"fá aðgang að IMS-símtalsþjónustu"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ee1b67d..d064684 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sono inclusi dati personali come numeri di carte di credito e password."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlla l\'ingrandimento del display"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlla il livello di zoom e la posizione del display."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Esegui gesti"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Consente di toccare, far scorrere, pizzicare ed eseguire altri gesti."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"disattivare o modificare la barra di stato"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ruolo di barra di stato"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Consente all\'applicazione di scattare foto e riprendere video con la fotocamera. Questa autorizzazione consente all\'applicazione di utilizzare la fotocamera in qualsiasi momento senza la tua conferma."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controllo vibrazione"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Consente all\'applicazione di controllare la vibrazione."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controllo flash"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Consente all\'applicazione di controllare il flash."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"chiamata diretta n. telefono"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Consente all\'applicazione di chiamare numeri di telefono senza il tuo intervento. Ciò può comportare chiamate o addebiti imprevisti. Tieni presente che ciò non consente all\'applicazione di chiamare numeri di emergenza. Applicazioni dannose potrebbero generare dei costi effettuando chiamate senza la tua conferma."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"Accesso al servizio di chiamata IMS"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index df0471e..375bdf0 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"כולל נתונים אישיים כמו מספרי כרטיס אשראי וסיסמאות."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"שליטה בהגדלת התצוגה"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"קבע את המרחק מהתצוגה ואת מיקום התצוגה."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ביצוע תנועות"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"יכול להקיש, להחליק, לעשות תנועת צביטה ולבצע תנועות אחרות."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"השבת או שנה את שורת המצב"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"מאפשר לאפליקציה להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"להיות שורת הסטטוס"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"מאפשר לאפליקציה לצלם תמונות וסרטונים באמצעות המצלמה. אישור זה מאפשר לאפליקציה להשתמש במצלמה בכל עת ללא אישורך."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"שליטה ברטט"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"מאפשר לאפליקציה לשלוט ברטט."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"שליטה בפנס"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"מאפשר לאפליקציה לשלוט בפנס."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"התקשר ישירות למספרי טלפון"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"מאפשר לאפליקציה להתקשר למספרי טלפון ללא התערבותך. פעולה זו עשויה לגרום לשיחות או לחיובים לא צפויים. שים לב שהדבר לא מאפשר לאפליקציה להתקשר למספרי חירום. אפליקציות זדוניות עשויות לגרום לעלויות על ידי ביצוע שיחות ללא התערבותך."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"‏גישה אל שירות שיחות IMS"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 764e0cf..a32a8ed 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"クレジットカードの番号やパスワードなどの個人データが含まれます。"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"画面の拡大の制御"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"画面のズームレベルと位置を制御します。"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"操作の実行"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"タップ、スワイプ、ピンチ、その他の操作を行えます。"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"ステータスバーの無効化や変更"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ステータスバーの無効化、システムアイコンの追加や削除をアプリに許可します。"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ステータスバーへの表示"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"カメラでの写真と動画の撮影をアプリに許可します。これにより、アプリが確認なしでいつでもカメラを使用できるようになります。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"バイブレーションの制御"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"バイブレーションの制御をアプリに許可します。"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ライトのコントロール"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ライトの制御をアプリに許可します。"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"電話番号発信"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"電話番号への自動発信をアプリに許可します。これにより、予期せぬ発信や料金が発生する可能性があります。なお、緊急通報番号への発信は許可されません。悪意のあるアプリが確認なしで発信し、料金が発生する恐れがあります。"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS通話サービスへのアクセス"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 0fa786c..f2fc953 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"შეიცავს ისეთ პირად მონაცემებს, როგორიცაა საკრედიტო ბარათის ნომრები და პაროლები."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ერანის გადიდების მართვა"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ეკრანის მასშტაბირების დონისა და პოზიციის მართვა."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ჟესტების შესრულება"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"შეუძლია შეხება, გადაფურცვლა, მასშტაბირება და სხვა ჟესტების შესრულება."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"სტატუსის ზოლის გათიშვა ან ცვლილება"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"აპს შეეძლება სტატუსების ზოლის გათიშვა და სისტემის ხატულების დამატება/წაშლა."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"სტატუსის ზოლის ჩანაცვლება"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"აპს შეეძლება კამერით სურათისა და ვიდეოს გადაღება. ეს ნებართვა აპს უფლებას აძლევს, ნებისმიერ დროს გამოიყენოს კამერა თქვენი დადასტურების გარეშე."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ვიბრაციის კონტროლი"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"აპს შეეძლება, მართოს ვიბრირება."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ფანრის მართვა"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"აპს შეეძლება, მართოს განათება."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"პირდაპირი დარეკვა ტელეფონის ნომრებზე"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"აპს შეეძლება დარეკოს ტელეფონის ნომრებზე თქვენი ჩარევის გარეშე. ამან შესაძლოა გამოიწვიოს თქვენს სატელეფონი ქვითარზე მოულოდნელი ხარჯებისა და ზარების გაჩენა. გაითვალისწინეთ, რომ აპს გადაუდებელი დახმარების ნომრებზე დარეკვა არ შეუძლია. მავნე აპებს შეეძლება თქვენი დადასტურების გარეშე ზარების განხორციელება და შესაბამისი საფასურის გადახდაც მოგიწევთ."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS ზარების სერვისზე წვდომა"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 275e5bc..ab82fd4 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит карта нөмірі және кілтсөздер сияқты жеке деректерді қоса."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дисплей ұлғайтуды басқару"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дисплейдің масштабтау деңгейін және орналастыруды басқару."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Қимылдарды орындау"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Түртуге, жанауға, қысуға және басқа қимылдарды орындауға болады."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"күйін көрсету тақтасын өшіру немесе өзгерту"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Қолданбаға күй жолағын өшіруге немесе жүйелік белгішелерді қосуға және жоюға рұқсат береді."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"күй жолағы болу"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Қолданбаға камераны қолданып, фотосурет немесе бейне жазу мүмкіндігін береді. Бұл рұқсат камераны кез келген уақытта сіздің құптауыңызды қажет етпей қолдану мүмкіндігін береді."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"тербелісті басқару"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Қолданбаға вибраторды басқаруға рұқсат береді."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"сигналдық шамды басқару"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Қолданбаға қалта шамын басқаруға рұқсат береді."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"нөмірлерге тікелей телефон шалу"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Қолданбаға сіздің қатысуыңызсыз қоңырау шалу мүмкіндігін береді. Нәтижесінде қосымша төлем немесе күтпеген қоңырау алуыңыз мүмкін. Есіңізде болсын, қолданба төтенше байланыстарға қоңырау шала алмайды. Залалды қолданбалар сіздің рұқсатыңызсыз қоңыраулар шалып, күтпеген төлемдерге себеп болуы мүмкін."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS қоңырау қызметін пайдалану"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 96abf45..ffb3599 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"រួម​បញ្ចូល​ទិន្នន័យ​ផ្ទាល់​ខ្លួន​ ដូចជា​លេខ​កាត​ឥណទាន និង​ពាក្យ​សម្ងាត់។"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"គ្រប់គ្រងការពង្រីកអេក្រង់"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"គ្រប់គ្រងការកំណត់ទីតាំង និងកម្រិតពង្រីករបស់អេក្រង់"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ធ្វើកាយវិការ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"អាចប៉ះ អូស ច្បិច និងធ្វើកាយវិការផ្សេងទៀត"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"បិទ ឬ​កែ​របារ​ស្ថានភាព"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ឲ្យ​កម្មវិធី​បិទ​របារ​ស្ថានភាព ឬ​បន្ថែម និង​លុប​រូប​តំណាង​ប្រព័ន្ធ។"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ធ្វើជារបារស្ថានភាព"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ឲ្យ​កម្មវិធី​ថត​រូប និង​វីដេអូ​ដោយ​ប្រើ​ម៉ាស៊ីន​ថត។ វា​ឲ្យ​កម្មវិធី​​ប្រើ​ម៉ាស៊ីន​ថត​នៅ​ពេល​​ណាមួយ​ដោយ​គ្មាន​ការ​បញ្ជាក់​របស់​អ្នក។"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ពិនិត្យ​ការ​ញ័រ"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ឲ្យ​កម្មវិធី​គ្រប់គ្រង​កម្មវិធី​ញ័រ។"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ត្រួតពិនិត្យ​ពិល"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ឲ្យ​កម្មវិធី​ពិនិត្យ​ពិល។"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ហៅ​លេខ​ទូរស័ព្ទ​ដោយ​ផ្ទាល់"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ឲ្យ​កម្មវិធី​ហៅ​លេខ​ទូរស័ព្ទ​ដោយ​គ្មាន​សកម្មភាព​របស់​អ្នក។​ វា​អាច​កាត់​លុយ​ ឬ​ហៅ​ដោយ​មិន​រំពឹង​ទុក។ ចំណាំ​ថា​ វា​មិន​អនុញ្ញាត​ឲ្យ​កម្មវិធី​ហៅ​លេខ​ពេល​អាសន្ន​ទេ។ កម្មវិធី​ព្យាបាទ​អាច​កាត់​លុយ​របស់​អ្នក​ ដោយ​ធ្វើការ​ហៅ​ដោយ​គ្មាន​ការ​បញ្ជាក់​របស់​អ្នក។"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"ចូលដំណើរការសេវាកម្មការហៅតាម IMS"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index d0dac23..ab7ad82 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಪಾಸ್‌ವರ್ಡ್‌ಗಳಂತಹ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ಪ್ರದರ್ಶನದ ವರ್ಧಕವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ಪ್ರದರ್ಶನದ ಝೂಮ್ ಮಟ್ಟ ಮತ್ತು ಸ್ಥಾನ ನಿರ್ಧಾರವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ಗೆಸ್ಚರ್‌ಗಳನ್ನು ಮಾಡಿ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ಟ್ಯಾಪ್ ಮಾಡಬಹುದು, ಸ್ವೈಪ್ ಮಾಡಬಹುದು, ಪಿಂಚ್ ಮಾಡಬಹುದು ಮತ್ತು ಇತರ ಗೆಸ್ಚರ್‌ಗಳನ್ನು ಮಾಡಬಹುದು."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇಲ್ಲವೇ ಮಾರ್ಪಡಿಸಿ"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಥವಾ ಸೇರಿಸಲು ಮತ್ತು ಸಿಸ್ಟಂ ಐಕಾನ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ಕ್ಯಾಮರಾ ಮೂಲಕ ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ನಿಮ್ಮ ಖಾತರಿ ಇಲ್ಲದೆಯೇ ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ವೈಬ್ರೇಷನ್‌‌ ನಿಯಂತ್ರಿಸಿ"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ವೈಬ್ರೇಟರ್‌ ನಿಯಂತ್ರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ಫ್ಲ್ಯಾಶ್‌ಲೈಟ್ ನಿಯಂತ್ರಿಸಿ"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ಫ್ಲ್ಯಾಶ್‌ಲೈಟ್ ನಿಯಂತ್ರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ಫೋನ್ ಸಂಖ್ಯೆಗಳಿಗೆ ನೇರವಾಗಿ ಕರೆ ಮಾಡಿ"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ನಿಮ್ಮ ಹಸ್ತಕ್ಷೇಪ ಇಲ್ಲದೆಯೇ ಫೋನ್‍ ಸಂಖ್ಯೆಗಳಿಗೆ ಕರೆ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಇದು ಅನಿರೀಕ್ಷಿತ ಶುಲ್ಕಗಳು ಅಥವಾ ಕರೆಗಳಿಗೆ ಕಾರಣವಾಗಬಹುದು. ತುರ್ತು ಸಂಖ್ಯೆಗಳಿಗೆ ಕರೆಮಾಡಲು ಈ ಅಪ್ಲಿಕೇಶನ್‍ ಅನುಮತಿಸುವುದಿಲ್ಲ ಎಂಬುದು ಗಮನದಲ್ಲಿರಲಿ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್‍‍ಗಳು ನಿಮ್ಮ ಖಾತರಿ ಇಲ್ಲದೆಯೇ ಕರೆಗಳನ್ನು ಮಾಡುವುದರ ಮೂಲಕ ನಿಮ್ಮ ಹಣ ಖರ್ಚಾಗಬಹುದು."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS ಕರೆ ಸೇವೆಯನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index b1b4588..4c478aa 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"신용카드 번호와 비밀번호 등의 개인 데이터를 포함합니다."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"디스플레이 배율 제어"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"디스플레이의 확대/축소 수준 및 위치를 제어합니다."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"동작 실행"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"탭, 스와이프, 확대/축소 및 기타 동작을 실행할 수 있습니다."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"상태 표시줄 사용 중지 또는 수정"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"앱이 상태 표시줄을 사용중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 허용합니다."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"상태 표시줄에 위치"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"앱이 카메라로 사진과 동영상을 찍을 수 있도록 허용합니다. 이 권한을 사용하면 앱이 언제든지 사용자의 확인 없이 카메라를 사용할 수 있습니다."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"진동 제어"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"앱이 진동을 제어할 수 있도록 허용합니다."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"카메라 플래시 제어"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"앱이 카메라 플래시를 제어할 수 있도록 허용합니다."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"전화번호 자동 연결"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"앱이 사용자의 조작 없이 전화번호로 전화를 걸 수 있도록 허용합니다. 이 경우 예상치 못한 통화 요금이 부과될 수 있습니다. 앱이 비상 전화를 걸도록 하는 권한은 주어지지 않습니다. 악성 앱이 사용자의 확인 없이 전화를 걸어 요금이 부과될 수 있습니다."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS 통화 서비스에 액세스"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 7644a9b..e9b01d1 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредиттик карта номурлары жана сырсөздөр сыяктуу өздүк берилиштерди камтыйт."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дисплейди чоңойтууну башкаруу"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Экрандагы сүрөттүн өлчөмүн өзгөртүү жана жайгаштыруу."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Жаңсоолорду аткаруу"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Таптап, серпип, чымчып жана башка жаңсоолорду аткара алат."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"абал тилкесин өчүрүү же өзгөртүү"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Колдонмого абал тилкесин өчүрүү же тутум сүрөтчөлөрүн кошуу же алып салуу мүмкүнчүлүгүн берет."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"абал тилкесинин милдетин аткаруу"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Колдонмого камера аркылуу видео жана сүрөт тартуу уруксатын берет. Бул уруксат, камераны каалаган убакта, сиздин ырастооңузсуз колдонуу уруксатын берет."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"титирөөнү башкаруу"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Колдонмого дирилдегичти көзөмөлдөө мүмкүнчүлүгүн берет."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"кол чыракты көзөмөлдөө"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Колдонмого колчыракты көзөмөлдөө мүмкүнчүлүгүн берет."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"телефон номерлерине түз чалуу"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Колдонмого сиздин катышууңузсуз телефон номурларга чалуу уруксатын берет. Бул сиз күтпөгөн чыгымдарга же чалууларга алып келиши мүмкүн. Бул куткаруучулардын номурларына чалууга уруксат бербей тургандыгын эске алыңыз. Зыяндуу колдонмолор, сиздин ырастооңузсуз чалууларды аткарып, көп чыгымдарга себепкер болушу мүмкүн."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS чалуу кызматына мүмкүнчүлүк алуу"</string>
diff --git a/core/res/res/values-land/config.xml b/core/res/res/values-land/config.xml
new file mode 100644
index 0000000..7308dc5
--- /dev/null
+++ b/core/res/res/values-land/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+    <integer name="config_dockedStackDividerSnapMode">2</integer>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index a5ff8d1..cf21944 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ຮວມທັງຂໍ້ມູນສ່ວນໂຕເຊັ່ນ: ເລກບັດເຄຣດິດ ແລະລະຫັດຜ່ານ."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ຄວບຄຸມການຂະຫຍາຍຈໍສະແດງຜົນ"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ຄວບຄຸມລະດັບການຊູມ ແລະການວາງຕຳແໜ່ງຂອງຈໍສະແດງຜົນ."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ດຳເນີນທ່າທາງຕ່າງໆ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ສາມາດແຕະ, ປັດນີ້ວມື, ຢິບນິ້ວມື ແລະ ດຳເນີນທ່າທາງອື່ນ."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"ປິດການນນຳໃຊ້ ຫຼື ແກ້ໄຂແຖບສະຖານະ"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງແຖບສະຖານະ ຫຼືເພີ່ມ ແລະລຶບໄອຄອນລະບົບອອກໄດ້."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ເປັນ​ແຖບ​ສະ​ຖາ​ນະ"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ອະນຸຍາດໃຫ້ແອັບຯຖ່າຍຮູບ ແລະວິດີໂອດ້ວຍກ້ອງຖ່າຍຮູບ. ການອະນຸຍາດນີ້ຈະອານຸຍາດໃຫ້ແອັບຯ ສາມາດໃຊ້ກ້ອງຖ່າຍຮູບໄດ້ຕະຫລອດເວລາ ໂດຍບໍ່ຕ້ອງຖ້າການຢືນຢັນຈາກທ່ານ."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ຄວບຄຸມການສັ່ນ"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ອະນຸຍາດໃຫ້ແອັບຯຄວບຄຸມໂຕສັ່ນ."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ຄວບຄຸມໄຟແຟລດ"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ອະນຸຍາດໃຫ້ແອັບຯ ຄວບຄຸມໄຟແຟລດ."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ໂທຫາເບີໂທລະສັບໂດຍກົງ"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ອະນຸຍາດໃຫ້ແອັບຯໂທຫາເບີໂທລະສັບໄດ້ ໂດຍບໍ່ຕ້ອງຖ້າການດຳເນີນການໃດໆຈາກທ່ານ. ຄຸນສົມບັດນີ້ອາດກໍ່ໃຫ້ເກີດຄ່າໃຊ້ຈ່າຍໃນການໂທທີ່ບໍ່ຄາດຄິດໄດ້. ໝາຍເຫດ: ຄຸນສົມບັດນີ້ບໍ່ໄດ້ເປັນການອະນຸຍາດໃຫ້ແອັບຯ ສາມາດໂທຫາເບີສຸກເສີນ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດເຮັດໃຫ້ທ່ານ ຕ້ອງເສຍຄ່າໂທໂດຍທີ່ບໍ່ໄດ້ຄາດຄິດ."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"ເຂົ້າ​ຫາ​ການ​ບໍ​ລິ​ການ​ໂທ IMS"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index f59e754..f1143fe 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Įtraukiami asmeniniai duomenys, pavyzdžiui, kredito kortelių numeriai ir slaptažodžiai."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekrano didinimo valdymas"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Valdykite ekrano mastelio keitimo lygį ir pozicijos nustatymą."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Veiksmai gestais"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Galima paliesti, perbraukti, suimti ir atlikti kitus veiksmus gestais."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"išjungti ar keisti būsenos juostą"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Leidžiama programai neleisti būsenos juostos arba pridėti ir pašalinti sistemos piktogramas."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"būti būsenos juosta"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Leidžiama programai fotografuoti ir filmuoti kamera. Šis leidimas suteikia teisę programai naudoti kamerą bet kada be jūsų patvirtinimo."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"valdyti vibraciją"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Leidžiama programai valdyti vibravimą."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"valdyti šviesos signalą"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Leidžiama programai valdyti šviesos signalą."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"skambinti tiesiogiai telefono numeriais"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Leidžiama programai skambinti telefonų numeriais be jūsų įsikišimo. Dėl to gali atsirasti nenumatytų apmokestinimų ar skambučių. Atminkite, kad programai neleidžiama skambinti pagalbos telefonų numeriais. Kenkėjiškos programos gali skambinti be jūsų patvirtinimo, o dėl to jums gali būti taikomi mokesčiai."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"pasiekti IMS skambučių paslaugą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index d1993ed..f864400 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -257,6 +257,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ietver personas datus, piemēram, kredītkartes numurus un paroles."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displeja palielinājuma kontrole"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolējiet displeja tālummaiņas līmeni un pozicionēšanu."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Žestu izpilde"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Atbalsta pieskaršanos, vilkšanu, savilkšanu un citus žestus."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"atspējot vai pārveidot statusa joslu"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Ļauj lietotnei atspējot statusa joslu vai pievienot un noņemt sistēmas ikonas."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"Būt par statusa joslu"</string>
@@ -355,8 +357,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ļauj lietotnei uzņemt attēlus un videoklipus ar kameru. Ar šo atļauju lietotne var jebkurā brīdī izmantot kameru bez jūsu apstiprinājuma."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrolēt vibrosignālu"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Ļauj lietotnei kontrolēt vibrosignālu."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrolēt uzliesmojumu"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Ļauj lietotnei kontrolēt zibspuldzi."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"tieši zvanīt uz tālruņa numuriem"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Ļauj lietotnei zvanīt uz tālruņa numuriem bez jūsu iejaukšanās. Tas var radīt neparedzētas izmaksas vai zvanus. Ņemiet vērā, ka lietotnei nav atļauts zvanīt uz tālruņa numuriem ārkārtas situācijām. Ļaunprātīgas lietotnes var radīt jums izmaksas, veicot zvanus bez jūsu apstiprinājuma."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"piekļūt tūlītējās ziņojumapmaiņas pakalpojumam, lai veiktu zvanus"</string>
diff --git a/core/res/res/values-mcc310-mnc160/strings.xml b/core/res/res/values-mcc310-mnc160/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200/strings.xml b/core/res/res/values-mcc310-mnc200/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210/strings.xml b/core/res/res/values-mcc310-mnc210/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220/strings.xml b/core/res/res/values-mcc310-mnc220/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc230/strings.xml b/core/res/res/values-mcc310-mnc230/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc230/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc240/strings.xml b/core/res/res/values-mcc310-mnc240/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc240/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc250/strings.xml b/core/res/res/values-mcc310-mnc250/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc250/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc270/strings.xml b/core/res/res/values-mcc310-mnc270/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc270/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc310/strings.xml b/core/res/res/values-mcc310-mnc310/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc310/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc490/strings.xml b/core/res/res/values-mcc310-mnc490/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc490/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc660/strings.xml b/core/res/res/values-mcc310-mnc660/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc660/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc800/strings.xml b/core/res/res/values-mcc310-mnc800/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc800/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- WFC Operator Error Codes -->
+    <string-array name="wfcOperatorErrorCodes" translatable="false">
+        <item>REG09</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+    </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 672c2b5..484e5a6 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Опфаќа лични податоци како што се броеви на кредитни картички и лозинки."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Контролирајте го зголемувањето на екранот"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Контролирајте го нивото на зумирање и позиционирање на екранот."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Користете движења"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Може да допрете, повлечете, штипнете и да користите други движења."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"оневозможи или измени статусна лента"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволува апликацијата да ја оневозможи статусната лента или да додава или отстранува системски икони."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"да стане статусна лента"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Овозможува апликацијата да прави фотографии и да снима видеа со камерата. Оваа дозвола овозможува апликацијата да ја користи камерата во кое било време без ваша потврда."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контролирај вибрации"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Дозволува апликацијата да ги контролира вибрациите."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"контролирај сијаличка"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Дозволува апликацијата да ја контролира батериската ламба."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"директно избирај телефонски броеви"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Овозможува апликацијата да повикува телефонски броеви без ваша интервенција. Ова може да предизвика неочекувани трошоци или повици. Имајте на ум дека ова не дозволува апликацијата да повикува броеви на служби за итна помош. Злонамерните апликации може да ве чинат пари поради повици без ваша потврда."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"пристапи до услугата за повици IMS"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index c6f6698..42a9a82 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ക്രെഡിറ്റ് കാർഡ് നമ്പറുകളും പാസ്‌വേഡുകളും പോലുള്ള വ്യക്തിഗത ഡാറ്റ ഉൾപ്പെടുന്നു."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ഡിസ്പ്ലേ മാഗ്നിഫിക്കേഷൻ നിയന്ത്രിക്കുക"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ഡിസ്പ്ലേയുടെ സൂം നിലയും പൊസിഷനിംഗും നിയന്ത്രിക്കുക."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ജെസ്‌റ്ററുകൾ നിർവഹിക്കുക"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ടാപ്പുചെയ്യാനോ സ്വൈപ്പുചെയ്യാനോ പിഞ്ചുചെയ്യാനോ മറ്റ് ജെസ്‌റ്ററുകൾ നിർവഹിക്കാനോ കഴിയും."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"സ്റ്റാറ്റസ് ബാർ പ്രവർത്തനരഹിതമാക്കുക അല്ലെങ്കിൽ പരിഷ്‌ക്കരിക്കുക"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"നില ബാർ പ്രവർത്തരഹിതമാക്കുന്നതിന് അല്ലെങ്കിൽ സിസ്‌റ്റം ഐക്കണുകൾ ചേർക്കുന്നതിനും നീക്കംചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ക്യാമറ ഉപയോഗിച്ച് ചിത്രങ്ങളും വീഡിയോകളും എടുക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ സ്ഥിരീകരണമില്ലാതെ ഏതുസമയത്തും ക്യാമറ ഉപയോഗിക്കാൻ ഈ അനുമതി അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"വൈബ്രേറ്റുചെയ്യൽ നിയന്ത്രിക്കുക"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"വൈബ്രേറ്റർ നിയന്ത്രിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ടോർച്ച് നിയന്ത്രിക്കുക"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ഫ്ലാഷ്ലൈറ്റിനെ നിയന്ത്രിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ഫോൺ നമ്പറുകളിലേക്ക് നേരിട്ട് വിളിക്കുക"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"നിങ്ങളുടെ ഇടപെടൽ ഇല്ലാതെ ഫോൺ നമ്പറുകളിലേക്ക് കോൾ ചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് അപ്രതീക്ഷിത നിരക്കുകൾക്കോ കോളുകൾക്കോ ഇടയാക്കാം. ഇത് അടിയന്തര നമ്പറുകളിലേക്ക് വിളിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക. ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ സ്ഥിരീകരണമില്ലാതെ കോളുകൾ ചെയ്യുന്നത് പണച്ചെലവിനിടയാക്കാം."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS കോൾ സേവനം ആക്സസ് ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index c42beca..91b9ea7 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит картын дугаар болон нууц үг зэрэг хувийн датаг агуулж байна."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дэлгэцийн өсгөлтийг хянах"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дэлгэцийн томруулах түвшин болон байршлыг хянах."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Зангах"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Товших, шудрах, жижигрүүлэх болон бусад зангааг хийх боломжтой."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"статус самбарыг идэвхгүй болгох болон өөрчлөх"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Апп нь статус самбарыг идэвхгүй болгох эсвэл систем дүрсийг нэмэх, хасах боломжтой."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"статусын хэсэг болох"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Апп нь камераар зураг авах болон видео бичих боломжтой. Энэ зөвшөөрөл нь апп-д ямар ч үед таны зөвшөөрөлгүйгээр камер ашиглах боломжийг олгоно."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"чичиргээг удирдах"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Апп нь чичиргээг удирдах боломжтой."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"гар чийдэн удирдах"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Апп нь гар чийдэнг удирдах боломжтой."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"утасны дугаарт шууд дуудлага хийх"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Апп нь таны оролцоогүйгээр дуудлага хийх боломжтой. Энэ нь төлөвлөгдөөгүй төлбөрт оруулах эсвэл дуудлага хийнэ. Энэ нь апп-г яаралтай дугаарт дуудлага хийхйг зөвшөөрөхгүй. Хортой апп нь таны зөвшөөрөлгүйгээр дуудлага хийж таныг төлбөрт оруулж болзошгүй"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS дуудлагын үйлчилгээнд хандах"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 8bc86e6..0cb37da 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर आणि संकेतशब्‍द यासारखा वैयक्तिक डेटा समाविष्‍ट करते."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन विस्तृतीकरण नियंत्रित करा"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनाचा झूम स्तर आणि स्थिती निर्धारण नियंत्रित करा."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"जेश्चर करा"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"टॅप, स्वाइप, पिंच आणि इतर जेश्चर करू शकते."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टीम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"कॅमेर्‍यासह चित्रे आणि व्हिडिओ घेण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपल्या पुष्टीकरणाशिवाय कोणत्याही वेळी कॅमेरा वापरण्यासाठी अॅप ला परवानगी देते."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"कंपन नियंत्रित करा"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"अॅप ला व्हायब्रेटर नियंत्रित करण्यासाठी अनुमती देते."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"फ्लॅशलाइट नियंत्रित करा"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"फ्लॅशलाइट नियंत्रित करण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"फोन नंबरवर प्रत्यक्ष कॉल करा"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"आपल्या हस्तक्षेपाशिवाय फोन नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देते. यामुळे अनपेक्षित शुल्क किंवा कॉल लागू शकतात. लक्षात ठेवा की हे आणीबाणीच्या नंबरवर कॉल करण्यासाठी अॅप ला अनुमती देत नाही. दुर्भावनापूर्ण अॅप्स नी आपल्या पुष्टिकरणाशिवाय कॉल केल्यामुळे आपले पैसे खर्च होऊ शकतात."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS कॉल सेवेमध्‍ये प्रवेश करा"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index f1249d6..d40c7d1 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Termasuk data peribadi seperti nombor kad kredit dan kata laluan."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kawal pembesaran paparan"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kawal tahap zum dan kedudukan paparan."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Lakukan gerak isyarat"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Boleh ketik, leret, cubit dan laksanakan gerak isyarat lain."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"lumpuhkan atau ubah suai bar status"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"jadi bar status"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Membenarkan apl mengambil gambar dan video menggunakan kamera. Kebenaran ini membenarkan apl untuk menggunakan kamera pada bila-bila masa tanpa pengesahan anda."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kawal getaran"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Membenarkan apl mengawal penggetar."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"mengawal lampu suluh"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Membenarkan apl mengawal lampu suluh."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"panggil terus nombor telefon"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Membenarkan apl memanggil nombor telefon tanpa campur tangan anda. Ini mungkin menyebabkan caj atau panggilan yang di luar jangkaan. Apl hasad boleh menyebabkan anda kerugian wang dengan membuat panggilan tanpa pengesahan anda."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"akses perkhidmatan panggilan IMS"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 3393e12..28a4d0f 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"အရေးကြီးသော ကိုယ်ရေးအချက်အလက်များဖြစ်တဲ့ ခရက်ဒစ်ကဒ်နံပါတ်များနှင့် စကားဝှက်များ ပါဝင်ပါတယ်."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"မျက်နှာပြင် ချဲ့ခြင်းကို ထိန်းချုပ်ပါ"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"မျက်နှာပြင် ချဲ့ခြင်းနှင့် နေရာချထားခြင်းကို ထိန်းချုပ်ပါ"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"လက်ဟန်များ အသုံးပြုပါ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"တို့ခြင်း၊ ပွတ်ဆွဲခြင်း၊ နှင့် အခြား လက်ဟန်များကို အသုံးပြုနိုင်ပါသည်။"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"အခြေအနေပြဘားအား အလုပ်မလုပ်ခိုင်းရန်သို့မဟုတ် မွမ်းမံရန်"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"appအား အခြေအနေပြ ဘားကို ပိတ်ခွင့် သို့မဟတ် စနစ် အိုင်ကွန်များကို ထည့်ခြင်း ဖယ်ရှားခြင်း ပြုလုပ်ခွင့် ပြုသည်။"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"အခြေအနေပြ ဘားဖြစ်ပါစေ"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"အပလီကေးရှင်းအား အလိုအလျောက် ဓာတ်ပုံရိုက်ခွင့်၊ ဗီဒီယို ရိုက်ကူးခွင့် ပြုပါ။ ဒီခွင့်ပြုချက်က အပလီကေးရှင်းကို အချိန်မရွေး ကင်မရာအား ခွင့်ပြုချက် မလိုအပ်ပဲ သုံးခွင့်ပြုပါသည်။"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"တုန်ခုန်မှုအား ထိန်းချုပ်ခြင်း"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"appအား တုန်ခါစက်ကို ထိန်းချုပ်ခွင့် ပြုသည်။"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ဓါတ်မီးအား ထိန်းသိမ်းရန်"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"appအား ကား ဖလက်ရှမီးကို ထိန်းချုပ်ခွင့် ပြုသည်။"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ဖုန်းနံပါတ်များကိုတိုက်ရိုက်ခေါ်ဆိုခြင်း"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"အပလီကေးရှင်းအား အလိုအလျောက် ဖုန်းခေါ်ခွင့် ပြုပါ။ မလိုအပ်သော ဖုန်းခ များ ဖြစ်ပေါ်နိုင်ပါသည်။ ဒီခွင့်ပြုခြင်းမှာ အရေးပေါ်ဖုန်းခေါ်ခြင်း မပါဝင်ပါ။ သံသယဖြစ်စရာ အပလီကေးရှင်းများက သင့်မသိပဲ ဖုန်းခေါ်ခြင်းဖြင့် ဖုန်းခ ပိုမိုကျနိုင်ပါသည်။"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS ဖုန်းခေါ်ဆိုမှု ဝန်ဆောင်ဌာန ဝင်ကြည့်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index e1ad565..7e909e4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data, som kredittkortnumre og passord."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollér forstørrelse for skjermen"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollér zoomenivået og plasseringen for skjermen."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gjøre bevegelser"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan trykke, sveipe, klype og gjøre andre bevegelser."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"deaktivere eller endre statusfeltet"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"vise appen i statusfeltet"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Appen tillates å ta bilder og filme med kameraet. Det betyr at appen kan bruke kameraet når som helst uten bekreftelse fra deg."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrollere vibreringen"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Lar appen kontrollere vibreringsfunksjonen."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrollere lommelykten"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Lar appen kontrollere lommelykten."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ringe telefonnummer direkte"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Lar appen ringe telefonnumre uten at du gjør noe. Dette kan resultere i uventede oppringninger og kostnader. Appen kan imidlertid ikke ringe nødnumre. Merk at skadelige apper kan påføre deg kostnader ved å ringe uten bekreftelse fra deg."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"få tilgang til nettprattjenesten for ringing"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index f94222b..15277a2 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"व्यक्तिगत डेटा जस्तै क्रेडिट कार्ड नम्बरहरू र पासवर्डहरू समावेश गर्दछ।"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन आवर्धन नियन्त्रण गर्नुहोस्"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनको जुम स्तर र स्थिति नियन्त्रण गर्नुहोस्।"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"इसाराहरू सम्बन्धी कार्य गर्नुहोस्"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ट्याप, स्वाइप गर्न, थिच्न र अन्य इसाराहरू सम्बन्धी कार्य गर्न सकिन्छ"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति पट्टिलाई अक्षम वा संशोधित गर्नुहोस्"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"स्थिति पट्टि असक्षम पार्न वा प्रणाली आइकनहरू थप्न र हटाउन अनुप्रयोगलाई अनुमति दिन्छ।"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"वस्तुस्थिति पट्टी हुन दिनुहोस्"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"अनुप्रयोगलाई क्यामेरासँग तस्बिर र भिडियोहरू लिन अनुमति दिन्छ। यस अनुमतिले अनुप्रयोगलाई तपाईंको पुष्टिकरण बिना कुनै पनि समयमा क्यामेरा प्रयोग गर्न स्वीकृति दिन्छ।"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"कम्पन नियन्त्रण गर्नुहोस्"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"अनुप्रयोगलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"फ्ल्यासलाईट नियन्त्रण गर्नुहोस्"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"फ्ल्यास प्रकाशलाई नियन्त्रण गर्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"फोन नम्बरहरूमा सिधै कल गर्नुहोस्"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न अनुप्रयोगलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले अनुप्रयोगलाई आपतकालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब अनुप्रयोगहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS कल सेवा पहुँच गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 1bef95d..b81b46e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omvat persoonlijke gegevens zoals creditcardnummers en wachtwoorden."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Schermvergroting bedienen"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Bedien het zoomniveau en de positionering van het scherm."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gebaren uitvoeren"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan tikken, vegen, samenknijpen en andere gebaren uitvoeren."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"statusbalk uitschakelen of wijzigen"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Hiermee kan de app de statusbalk uitschakelen of systeempictogrammen toevoegen en verwijderen."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"de statusbalk zijn"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Hiermee kan de app foto\'s en video\'s maken met de camera. Met deze toestemming kan de app de camera altijd gebruiken, zonder je bevestiging."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"trilling beheren"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Hiermee kan de app de trilstand beheren."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"zaklamp bedienen"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Hiermee kan de app de zaklamp bedienen."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefoonnummers rechtstreeks bellen"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Hiermee kan de app zonder je tussenkomst telefoonnummers bellen. Dit kan tot onverwachte kosten of oproepen leiden. De app kan hiermee geen noodnummers bellen. Schadelijke apps kunnen u geld kosten door nummers te bellen zonder om je bevestiging te vragen."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"toegang tot IMS-service voor bellen"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 168c66f..192c181 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ਇਸ ਵਿੱਚ ਨਿੱਜੀ ਡਾਟਾ ਸ਼ਾਮਲ ਹੈ ਜਿਵੇਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ ਅਤੇ ਪਾਸਵਰਡ।"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ਡਿਸਪਲੇ ਵੱਡਦਰਸ਼ੀ ਨੂੰ ਨਿਯੰਤ੍ਰਿਤ ਕਰੋ"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ਡਿਸਪਲੇ ਦੇ ਜ਼ੂਮ ਪੱਧਰ ਅਤੇ ਸਥਿਤੀ ਨੂੰ ਨਿਯੰਤ੍ਰਿਤ ਕਰੋ।"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ਸੰਕੇਤ ਕਰਦੀ ਹੈ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ਟੈਪ ਕਰ ਸਕਦੀ ਹੈ, ਸਵਾਈਪ ਕਰ ਸਕਦੀ ਹੈ, ਪਿੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਅਤੇ ਹੋਰ ਸੰਕੇਤ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"ਸਥਿਤੀ ਬਾਰ ਅਸਮਰੱਥ ਬਣਾਓ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ਐਪ ਨੂੰ ਸਥਿਤੀ ਬਾਰ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਉਣ ਜਾਂ ਸਿਸਟਮ ਆਈਕਨਾਂ ਨੂੰ ਜੋੜਨ ਅਤੇ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ਸਥਿਤੀ ਪੱਟੀ ਬਣਨ ਦਿਓ"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ਐਪ ਨੂੰ ਕੈਮਰੇ ਨਾਲ ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਲੈਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਕੈਮਰਾ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ਵਾਈਬ੍ਰੇਸ਼ਨ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ਐਪ ਨੂੰ ਵਾਈਬ੍ਰੇਟਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ਫਲੈਸ਼ਲਾਈਟ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ਐਪ ਨੂੰ ਫਲੈਸ਼ਲਾਈਟ ਤੇ ਨਿਯੰਤਰਣ ਪਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ਫੋਨ ਨੰਬਰਾਂ ਤੇ ਸਿੱਧੇ ਕਾਲ ਕਰੋ"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਫੋਨ ਨੰਬਰਾਂ ਤੇ ਕਾਲ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਸਦੇ ਸਿੱਟੇ ਵਜੋਂ ਅਕਲਪਿਤ ਖ਼ਰਚੇ ਜਾਂ ਕਾਲਾਂ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਧਿਆਨ ਦਿਓ ਕਿ ਇਹ ਐਪ ਨੂੰ ਐਮਰਜੈਂਸੀ ਨੰਬਰਾਂ ਤੇ ਕਾਲ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦਾ। ਖ਼ਰਾਬ ਐਪਸ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਾਲਾਂ ਕਰਕੇ ਤੁਹਾਨੂੰ ਖ਼ਰਚੇ ਪਾ ਸਕਦੇ ਹਨ।"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS ਕਾਲ ਸੇਵਾ ਤੱਕ ਪਹੁੰਚ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 80ef7df..38437fa 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obejmuje informacje osobiste, takie jak numery kart kredytowych i hasła."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Regulowanie powiększenia ekranu"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Regulowanie poziomu i obszaru powiększenia ekranu."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Obsługa gestów"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Obsługuje kliknięcia, przesunięcia, ściągnięcia palców i inne gesty."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"wyłączanie lub zmienianie paska stanu"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"działanie jako pasek stanu"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Pozwala aplikacji na robienie zdjęć i nagrywanie filmów przy użyciu aparatu. Aplikacja z tym uprawnieniem może użyć aparatu w dowolnym momencie bez Twojego potwierdzenia."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"sterowanie wibracjami"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Pozwala aplikacji na sterowanie wibracjami."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrolowanie latarki"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Pozwala aplikacji na sterowanie latarką."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"bezpośrednie wybieranie numerów telefonów"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Pozwala aplikacji na dzwonienie pod numery telefonów bez Twojej wiedzy. Może to skutkować nieoczekiwanymi opłatami lub połączeniami. Aplikacja nie może dzwonić pod numery alarmowe. Złośliwe aplikacje mogą generować koszty, wykonując połączenia bez Twojego potwierdzenia."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"usługa telefoniczna z dostępem do komunikatora"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 11e060a..91821a4 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliação da tela"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o posicionamento e nível de zoom da tela."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Fazer gestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Toque, deslize, faça gestos de pinça e faça outros gestos."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que o app tire fotos e filme vídeos com a câmera. Esta permissão autoriza o app a usar a câmera a qualquer momento sem sua confirmação."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite que o app controle a vibração."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controlar lanterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite que o app controle a lanterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ligar diretamente para números de telefone"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite que o app ligue para números de telefone sem sua intervenção. Isso pode resultar em cobranças ou chamadas inesperadas. Esta opção não permite que o app ligue para números de emergência. Apps maliciosos podem gerar custos com chamadas feitas sem sua confirmação."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"acessar serviço de mensagens instantâneas para chamadas"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index d76133a..f37d3a6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartões de crédito e palavras-passe."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar a ampliação do ecrã"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o nível de zoom e o posicionamento do ecrã."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Realizar gestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"É possível tocar, deslizar rapidamente, juntar os dedos e realizar outros gestos"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar barra de estado"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite à aplicação desativar a barra de estado ou adicionar e remover ícones do sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ser apresentada na barra de estado"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que a aplicação tire fotografias e grave vídeos com a câmara. Esta autorização permite que a aplicação utilize a câmara sem a sua confirmação em qualquer altura."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite à aplicação controlar o vibrador."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controlar lanterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite à aplicação controlar a lanterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"marcar números de telefone diretamente"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite que a aplicação ligue para números de telefone sem a intervenção do utilizador. Esta ação pode resultar em cobranças ou chamadas inesperadas. Tenha em atenção que isto não permite que a aplicação ligue para números de emergência. As aplicações maliciosas podem fazer com que incorra em custos, fazendo chamadas sem a sua confirmação."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"aceder ao serviço de chamadas IMS"</string>
@@ -1478,7 +1478,7 @@
       <item quantity="one">Durante 1 h</item>
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
-    <string name="zen_mode_alarm" msgid="9128205721301330797">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
+    <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"Até que o utilizador desative"</string>
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até desativar Não incomodar"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 11e060a..91821a4 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliação da tela"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o posicionamento e nível de zoom da tela."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Fazer gestos"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Toque, deslize, faça gestos de pinça e faça outros gestos."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que o app tire fotos e filme vídeos com a câmera. Esta permissão autoriza o app a usar a câmera a qualquer momento sem sua confirmação."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite que o app controle a vibração."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"controlar lanterna"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite que o app controle a lanterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ligar diretamente para números de telefone"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite que o app ligue para números de telefone sem sua intervenção. Isso pode resultar em cobranças ou chamadas inesperadas. Esta opção não permite que o app ligue para números de emergência. Apps maliciosos podem gerar custos com chamadas feitas sem sua confirmação."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"acessar serviço de mensagens instantâneas para chamadas"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8a78f17..b235b7a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -257,6 +257,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Include date personale, cum ar fi numere ale cardurilor de credit sau parole."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlați mărirea pe afișaj"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlați nivelul de zoom și poziționarea afișajului."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Folosiți gesturi"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Se poate atinge, glisa, ciupi și se pot folosi alte gesturi."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"dezactivare sau modificare bare de stare"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"să fie bara de stare"</string>
@@ -355,8 +357,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite aplicației să realizeze fotografii și videoclipuri cu camera foto. Cu această permisiune aplicația utilizează camera foto oricând și fără confirmare."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlează vibrarea"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite aplicației să controleze mecanismul de vibrare."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"control lanternă"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Permite aplicației să controleze lanterna."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"apelare directă numere de telefon"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite aplicației să apeleze numere de telefon fără intervenţia dvs. Acest lucru poate determina apariția unor taxe sau a unor apeluri neaşteptate. Cu această permisiune aplicația nu poate apela numerele de urgenţă. Aplicaţiile rău intenţionate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"accesează serviciul de apelare IMS"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0753e93..0c5fb09b 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"В том числе личные данные, например номера кредитных карт и пароли."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управлять масштабом изображения"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управлять позиционированием и размером изображения на экране."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Жесты"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Может выполнять жесты нажатия, пролистывания, масштабирования и т. д."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"Отключение/изменение строки состояния"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"Замена строки состояния"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Приложение сможет снимать фотографии и видеоролики с помощью камеры в любое время без вашего разрешения."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"Управление функцией вибросигнала"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Приложение сможет контролировать вибросигналы."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"Управление вспышкой"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Приложение сможет контролировать вспышку."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"Осуществление телефонных вызовов"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Приложение сможет без вашего участия звонить на любой номер телефона. Это не относится к номерам экстренных служб. Вредоносные программы смогут совершать вызовы без вашего разрешения, что может привести к непредвиденным расходам."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"совершение звонков с помощью службы IMS"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index d66b245..17f0f97 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ණයවරපත් අංක සහ මුරපද වැනි පුද්ගලික දත්ත ඇතුළත් වේ."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශන විශාලන මට්ටම පාලනය කිරීම"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කිරීම."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"අභින සිදු කරන්න"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"තට්ටු කිරීමට, ස්වයිප් කිරීමට, පින්ච් කිරීමට, සහ වෙනත් අභින සිදු කිරීමට හැකිය."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"තත්ව තීරුව අක්‍රිය කිරීමට හෝ පද්ධති නිරූපක එකතු හෝ ඉවත් කිරීමට යෙදුමට අවසර දේ."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"තත්ත්ව තීරුව බවට පත්වීම"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"කැමරාවෙන් පින්තූර ගැනීමට සහ වීඩියෝ කිරීමට යෙදුමට අවසර දෙන්න. මෙම අවසරය මඟින් ඔබගේ අනුදැනුමකින් තොරව ඕනෑම වේලාවකදී කැමරාව භාවිතා කිරීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"කම්පනය පාලනය කිරීම"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"කම්පකය පාලනයට යෙදුමට අවසර දෙන්න."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"සැණෙළි ආලෝකය පාලනය කරන්න"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"සැණෙළිය පාලනයට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"දුරකථන අංක වෙත ඍජුවම අමතන්න"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ඔබගේ මැදිහත් වීමක් නොමැතිව දුරකථන අංක ඇමතීමට යෙදුමට අවසර දෙන්න. මෙහි ප්‍රතිඑලය වන්නේ අනපේක්ෂිත අයකිරීම් හෝ ඇමතුම් ඇතිවීමයි. මෙයන් හදිසි අංක වලට ඇමතුම් ගැනීමට යෙදුමට අවසර නොදෙන බවට සටහන් කරගන්න. ඔබගේ අනුදැනුමක් නොමැතිව ඇමතුම් ගැනීමෙන් අනිෂ්ට යෙදුම් ඔබගේ මුදල් නිකරුණේ වැය කරයි."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS ඇමතුම් සේවාවට පිවිසෙන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 05c8467..d9c79f2 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledovanie zahŕňa osobné údaje ako sú čísla kreditných kariet a heslá."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ovládanie priblíženia obrazovky"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ovládajte úroveň priblíženia/oddialenia obrazovky a umiestnenie"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gestá"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Je možné použiť klepnutie, prejdenie, stiahnutie prstami a ďalšie gestá."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"zakázanie alebo zmeny stavového riadka"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"vydávanie sa za stavový riadok"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Umožňuje aplikácii fotografovať a nahrávať videá pomocou fotoaparátu. Toto povolenie umožňuje aplikácii používať fotoaparát kedykoľvek a bez vášho potvrdenia."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládať vibrovanie"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikácii ovládať vibrácie."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ovládať kontrolku"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Umožňuje aplikácii ovládať svetlo."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"priamo volať na telefónne čísla"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Umožňuje aplikácii volať telefónne čísla bez vášho zásahu. V dôsledku toho sa môžu účtovať neočakávané poplatky alebo sa môžu uskutočniť neočakávané hovory. Toto povolenie neumožňuje aplikácii volať na čísla tiesňového volania."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"prístup k službe volania IMS"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index ea5f4c4..8e293e1 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Vključuje osebne podatke, kot so številke kreditnih kartic in gesla."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Nadziranje povečave prikaza"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Nadziranje stopnje povečave in položaja prikaza."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Izvajanje potez"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Mogoče je izvajanje dotikov, vlečenja, vlečenja prstov skupaj in drugih potez."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"onemogočanje ali spreminjanje vrstice stanja"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikacijam omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"postane vrstica stanja"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Aplikaciji omogoča fotografiranje in snemanje videoposnetkov s kamero. S tem dovoljenjem lahko aplikacija kadar koli uporablja kamero brez vaše potrditve."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"nadzor vibriranja"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Aplikaciji omogoča nadzor vibriranja."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"nadzor svetilke"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Aplikaciji omogoča nadzor svetilke."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"neposredno klicanje telefonskih številk"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Aplikaciji omogoča klicanje telefonskih številk brez vašega posredovanja. Zaradi tega lahko pride do nepričakovanih stroškov ali klicev. Aplikaciji to ne dovoljuje opravljanja klicev v sili. Zlonamerne aplikacije lahko kličejo brez vaše potrditve, kar vas lahko drago stane."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"dostop do storitve za klicanje IMS"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 77e0e62..a24e82f 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Përfshi të dhënat personale si numrat e kartave të kreditit si dhe fjalëkalimet."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollo zmadhimin e ekranit"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollo nivelin dhe pozicionimin e zmadhimit të ekranit."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Kryen gjeste"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Mund të trokasë, rrëshqasë, bashkojë gishtat dhe kryejë gjeste të tjera."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"çaktivizo ose modifiko shiritin e statusit"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"të bëhet shiriti i statusit"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Lejon aplikacion të krijojë fotografi dhe video me kamerën. Kjo leje mundëson përdorimin e kamerës në çdo kohë pa konfirmimin tënd."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrollo dridhjen"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Lejon aplikacionin të kontrollojë dridhësin."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrollo elektrikun"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Lejon aplikacionin të kontrollojë elektrikun."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefono drejtpërdrejt numrat e telefonit"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Lejon aplikacionin të telefonojë numra pa ndërhyrjen tënde. Kjo mund të rezultojë në tarifa ose telefonata të papritura. Ki parasysh se kjo nuk e lejon aplikacionin të telefonojë numra urgjence. Aplikacione keqdashëse mund të të kushtojnë para duke kryer telefonata pa konfirmimin tënd."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"qasje në shërbimin e telefonatave IMS"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 2423418..c706264 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -257,6 +257,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Обухвата личне податке као што су бројеви кредитних картица и лозинке."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управљај увећањем приказа"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управља нивоом зумирања приказа и одређивањем положаја."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Обављање покрета"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Може да додирује, листа, скупља приказ и обавља друге покрете."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"онемогућавање или измена статусне траке"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"функционисање као статусна трака"</string>
@@ -355,8 +357,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Дозвољава апликацији да снима слике и видео снимке камером. Ова дозвола омогућава апликацији да у било ком тренутку користи камеру без ваше потврде."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контрола вибрације"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Дозвољава апликацији да контролише вибрацију."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"контрола осветљења"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Дозвољава апликацији да контролише блиц."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"директно позивање бројева телефона"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Дозвољава апликацији да позива бројеве телефона без ваше дозволе. Ово може да доведе до неочекиваних трошкова или позива. Имајте на уму да ово не дозвољава апликацији да позива бројеве за хитне случајеве. Злонамерне апликације могу да позивају без ваше потврде, што може да доведе до трошкова."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"приступ услузи позива помоћу размене тренутних порука"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cdf252e..ecc6d4e 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omfattar personuppgifter som kreditkortsnummer och lösenord."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Styr skärmförstoringen"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Styr skärmens zoomnivå och positionering."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Göra rörelser"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan trycka, svepa, nypa och göra andra rörelser."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"inaktivera eller ändra statusfält"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"visas i statusfältet"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Tillåter att appen tar bilder och spelar in videor med kameran. Med den här behörigheten tillåts appen att använda kameran när som helst utan ditt godkännande."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"styra vibration"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Tillåter att appen styr vibrationen."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"styra lampa"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Tillåter att appen styr lampan."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ringa telefonnummer direkt"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Tillåter att appen ringer telefonnummer utan någon aktiv åtgärd från dig. Detta kan leda till oväntade avgifter och samtal. Observera att appen inte tillåts ringa nödsamtal. Skadliga appar kan ringa utan ditt godkännande och detta kan kosta pengar."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"tillgång till tjänsten för snabbmeddelanden vid samtal"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 5e618bf..d1e6d97 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inajumuisha data binafsi kama vile nambari za kadi ya mkopo na manenosiri."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Dhibiti ukuzaji wa onyesho"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Dhibiti kiwango cha kukuza na nafasi cha onyesho."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Tekeleza ishara"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Unaweza kugonga, kutelezesha kidole, kubana na kutekeleza ishara zingine."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"zima au rekebisha mwambaa hali"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa ikoni za mfumo."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"kuwa sehemu ya arifa"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Inaruhusu programu kupiga picha na video kwa kamera. Kibali hiki kinaruhusu programu kutumia kamera kwa wakati wowote bila uthibitisho wako."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"Kudhibiti mtetemo"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Inaruhusu programu kudhibiti kitingishi."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"dhibiti tochi"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Inaruhusu programu kudhibiti tochi."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"piga simu moja kwa moja kwa nambari za simu"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Inaruhusu programu kupiga nambari za simu bila ya wewe kuingilia kati. Hii inaweza kusababisha gharama zisizotarajiwa au simu. Kumbuka kuwa hii hairuhusu programu kupiga nambari za dharura. Programu hasidi zinaweza kukugharimu pesa kwa kupiga simu bila uthibitisho wako."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"fikia huduma ya simu ya IMS"</string>
@@ -795,7 +795,7 @@
     <string name="save_password_remember" msgid="6491879678996749466">"Kumbuka"</string>
     <string name="save_password_never" msgid="8274330296785855105">"Katu"</string>
     <string name="open_permission_deny" msgid="7374036708316629800">"Hauna idhini ya kufungua ukurasa huu."</string>
-    <string name="text_copied" msgid="4985729524670131385">"Maandishi yamenakiliwa kwenye ubao klipu."</string>
+    <string name="text_copied" msgid="4985729524670131385">"Maandishi yamenakiliwa kwenye ubao wa kunakili."</string>
     <string name="more_item_label" msgid="4650918923083320495">"Zaidi"</string>
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"Menyu+"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"mwanya"</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 5a007ce..7f57ded 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -40,5 +40,6 @@
     <!-- Use a larger scaling span for larger screen devices. -->
     <dimen name="config_minScalingSpan">32mm</dimen>
 
+    <integer name="config_dockedStackDividerSnapMode">1</integer>
 </resources>
 
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 7e0ab5e..eacc9a9 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"கிரெடிட் கார்டு எண்கள் மற்றும் கடவுச்சொற்கள் போன்ற தனிப்பட்ட தகவலும் உள்ளடங்கும்."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"திரையின் உருப்பெருக்கத்தைக் கட்டுப்படுத்துதல்"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"திரையின் ஜூம் அளவையும் நிலையையும் கட்டுப்படுத்தலாம்."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"சைகைகளைச் செயல்படுத்துதல்"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"தட்டலாம், ஸ்வைப் செய்யலாம், பின்ச் செய்யலாம் மற்றும் பிற சைகைகளைச் செயல்படுத்தலாம்."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"நிலைப் பட்டியை முடக்குதல் அல்லது மாற்றுதல்"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"நிலைப் பட்டியை முடக்க அல்லது முறைமையில் ஐகான்களைச் சேர்க்க மற்றும் அகற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"நிலைப் பட்டியில் இருக்கும்"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"கேமரா மூலமாகப் படங்களையும், வீடியோக்களையும் எடுக்க பயன்பாட்டை அனுமதிக்கிறது. உங்கள் உறுதிப்படுத்தல் இன்றி கேமராவை எந்நேரத்திலும் பயன்படுத்தப் பயன்பாட்டை இது அனுமதிக்கிறது."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"அதிர்வைக் கட்டுப்படுத்துதல்"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"அதிர்வைக் கட்டுப்படுத்தப் பயன்பாட்டை அனுமதிக்கிறது."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ஃப்லாஷ்லைட்டை இயக்குதல்"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ஃப்ளாஷ் லைட்டைக் கட்டுப்படுத்த, பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"தொலைபேசி எண்களை நேரடியாக அழைத்தல்"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"உங்கள் தலையீட்டின்றி மொபைல் எண்களை அழைக்கப் பயன்பாட்டை அனுமதிக்கிறது. இதன் விளைவாக எதிர்பாராத கட்டணங்களோ அழைப்புகளோ ஏற்படலாம். அவசரகால எண்களை அழைக்க இது பயன்பாட்டை அனுமதிக்காது என்பதை நினைவில்கொள்ளவும். தீங்கிழைக்கும் பயன்பாடுகள், உங்கள் உறுதிப்படுத்தல் இன்றி அழைப்புகளைச் செய்வதால் உங்களுக்குச் செலவு ஏற்படக்கூடும்."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS அழைப்புச் சேவையை அணுகுதல்"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 87b782f..0440134 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"క్రెడిట్ కార్డు నంబర్‌లు మరియు పాస్‌వర్డ్‌ల వంటి వ్యక్తిగత డేటాను కలిగి ఉంటుంది."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"డిస్‌ప్లే మాగ్నిఫికేషన్‌ను నియంత్రించండి"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"డిస్‌ప్లే జూమ్ స్థాయి మరియు స్థానాన్ని నియంత్రిస్తుంది."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"సంజ్ఞలను చేయడం"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"నొక్కగలరు, స్వైప్ చేయగలరు, స్క్రీన్‌పై రెండు వేళ్లను ఉంచి ఆ వేళ్లను దగ్గరకు లేదా దూరానికి లాగగలరు మరియు ఇతర సంజ్ఞలను చేయగలరు."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"స్థితి బార్‌ను నిలిపివేయడం లేదా సవరించడం"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"స్థితి బార్‌ను నిలిపివేయడానికి లేదా సిస్టమ్ చిహ్నాలను జోడించడానికి మరియు తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"స్థితి పట్టీగా ఉండటం"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"కెమెరాతో చిత్రాలు మరియు వీడియోలను తీయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఈ అనుమతి మీ నిర్ధారణ లేకుండానే ఎప్పుడైనా కెమెరాను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"వైబ్రేషన్‌ను నియంత్రించడం"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"వైబ్రేటర్‌ను నియంత్రించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ఫ్లాష్‌కాంతిని నియంత్రించడం"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ఫ్లాష్‌లైట్‌ను నియంత్రించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ఫోన్ నంబర్‌లకు నేరుగా కాల్ చేయడం"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"మీ ప్రమేయం లేకుండా ఫోన్ నంబర్‌లకు కాల్ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. దీని వలన అనుకోని ఛార్జీలు విధించబడవచ్చు లేదా కాల్‌లు రావచ్చు. ఇది అత్యవసర నంబర్‌లకు కాల్ చేయడానికి అనువర్తనాన్ని అనుమతించదని గుర్తుంచుకోండి. హానికరమైన అనువర్తనాలు మీ నిర్ధారణ లేకుండానే కాల్‌లు చేయడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS కాల్ సేవ ప్రాప్యత అనుమతి"</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3435474..0f98cfb 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -23,4 +23,12 @@
 
     <!-- Flags enabling default window features. See Window.java -->
     <bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
+
+    <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
+    <string translatable="false" name="config_defaultPictureInPictureBounds">"1420 100 1820 325"</string>
+
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
+         is located in center. -->
+    <string translatable="false" name="config_centeredPictureInPictureBounds">"600 331 1320 749"</string>
+
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 2ec0f4c..5d27943 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"รวมถึงข้อมูลส่วนบุคคล เช่น หมายเลขบัตรเครดิตและรหัสผ่าน"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ควบคุมการขยายการแสดงผล"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ควบคุมระดับการซูมและการวางตำแหน่งของการแสดงผล"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ทำท่าทางสัมผัส"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"สามารถแตะ เลื่อน บีบ และทำท่าทางสัมผัสอื่นๆ"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"ปิดการใช้งานหรือแก้ไขแถบสถานะ"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"อนุญาตให้แอปพลิเคชันปิดใช้งานแถบสถานะหรือเพิ่มและนำไอคอนระบบออก"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"เป็นแถบสถานะ"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"อนุญาตให้แอปพลิเคชันถ่ายภาพและวิดีโอด้วยกล้องถ่ายรูปนี้ การอนุญาตนี้จะทำให้แอปพลิเคชันสามารถใช้กล้องถ่ายรูปได้ทุกเมื่อโดยไม่ต้องรอการยืนยันจากคุณ"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ควบคุมการสั่นเตือน"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"อนุญาตให้แอปพลิเคชันควบคุมการสั่นเตือน"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"ควบคุมไฟฉาย"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"อนุญาตให้แอปพลิเคชันควบคุมไฟฉาย"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"โทรติดต่อหมายเลขโทรศัพท์โดยตรง"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"อนุญาตให้แอปพลิเคชันโทรเข้าโทรศัพท์โดยไม่ต้องให้คุณจัดการ ซึ่งอาจทำให้มีการเรียกเก็บเงินหรือการโทรที่ไม่คาดคิด โปรดทราบว่าการทำงานนี้ไม่ได้อนุญาตให้แอปพลิเคชันโทรไปหมายเลขฉุกเฉิน แอปพลิเคชันที่เป็นอันตรายอาจทำให้คุณต้องเสียค่าบริการด้วยการโทรโดยไม่ขอการยืนยันจากคุณ"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"เข้าถึงบริการโทร IMS"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 36aa9d9..479efdf 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"May kasamang personal na data tulad ng mga numero ng credit card at password."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolin ang pag-magnify ng display"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolin ang antas ng pag-zoom at pagpoposisyon ng display."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Magsagawa ng mga galaw"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"May kakayahang mag-tap, mag-swipe, mag-pinch at magsagawa ng iba pang mga galaw."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"huwag paganahin o baguhin ang status bar"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Pinapayagan ang app na huwag paganahin ang status bar o magdagdag at mag-alis ng mga icon ng system."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"maging status bar"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Pinapayagan ang app na kumuha ng mga larawan at video gamit ang camera. Pinapayagan ng pahintulot na ito ang app na gamitin ang camera anumang oras nang wala ng iyong kumpirmasyon."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kontrolin ang pag-vibrate"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Pinapayagan ang app na kontrolin ang vibrator."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kontrolin ang flashlight"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Pinapayagan ang app na kontrolin ang flashlight."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"direktang tawagan ang mga numero ng telepono"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Pinapayagan ang app na tumawag sa mga numero ng telepono nang wala ng iyong panghihimasok. Maaari itong magresulta sa mga hindi inaasahang pagsingil o tawag. Tandaan na hindi nito pinapayagan ang app na tumawag sa mga numerong pang-emergency. Maaaring magpagastos sa iyo ng pera ang nakakahamak na apps sa pamamagitan ng pagtawag nang wala ng iyong kumpirmasyon."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"i-access ang serbisyo sa tawag ng IMS"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7b0bdc6..b0c6d5a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredi kartı ve şifre gibi kişisel bilgiler içerir."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekran büyütecini kontrol et"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekranın yakınlaştırma seviyesini ve konumunu kontrol edin."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Haraketleri yapma"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Hafifçe dokunabilir, hızlıca kaydırabilir, sıkıştırabilir ve diğer hareketleri yapabilirsiniz."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"durum çubuğunda olma"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Uygulamaya kamerayla fotoğraf ve video çekme izni verir. Bu izin, uygulamanın sizin onayınız olmadan istediği zaman kamerayı kullanmasına olanak sağlar."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"titreşimi denetleme"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Uygulamaya, titreşimi denetleme izni verir."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"el fenerini denetle"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Uygulamaya, el fenerini denetleme izni verir."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefon numaralarına doğrudan çağrı yap"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Uygulamaya sizin müdahaleniz olmadan telefon numaralarına çağrı yapma izni verir. Bu durum beklenmeyen ödemelere veya çağrılara neden olabilir. Ancak bu iznin, uygulamanın acil numaralara çağrı yapmasına olanak sağlamadığını unutmayın. Kötü amaçlı uygulamalar onayınız olmadan çağrılar yaparak sizi zarara sokabilir."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS çağrı hizmetine erişme"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 308e50c..af1354a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -258,6 +258,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включає особисті дані, як-от номери кредитних карток і паролі."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Контролювати збільшення екрана"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Контролювати масштаб і розташування екрана."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Виконання жестів"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Можна торкатися, проводити пальцем, стискати пальці та виконувати інші жести."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"вимикати чи змін. рядок стану"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволяє програмі вимикати рядок стану чи додавати та видаляти піктограми системи."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"відображатися як рядок стану"</string>
@@ -356,8 +358,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Дозволяє програмі фотографувати та знімати відео за допомогою камери. Такий дозвіл дає програмі змогу будь-коли використовувати камеру без вашого підтвердження."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"контролювати вібросигнал"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Дозволяє програмі контролювати вібросигнал."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"контр. блим. світло"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Дозволяє програмі контролювати світловий сигнал."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"прямо набирати номери тел."</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Дозволяє програмі набирати номери телефону без вашого відома. Це може спричинити неочікуване стягнення плати чи здійснення дзвінків. Зауважте, що це не дозволяє програмі набирати екстрені номери. Шкідливі програми можуть здійснювати дзвінки без вашого підтвердження, за що з вас стягуватимуться кошти."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"отримувати доступ до телефонної служби IMS"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index ca26e9b..82bf7ab 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اس میں ذاتی ڈیٹا جیسے کریڈٹ کارڈ نمبرز اور پاس ورڈز شامل ہیں۔"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ڈسپلے بڑا کرنے کے عمل کو کنٹرول کریں"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ڈسپلے کے زوم کی سطح اور پوزیشن کو کنٹرول کریں۔"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"اشارے انجام دیں"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"تھپتھپانا، سوائپ کرنا، چٹکی بھرنا اور دیگر اشارے انجام دے سکتی ہے"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"اسٹیٹس بار کو غیر فعال یا اس میں ترمیم کریں"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"ایپ کو اسٹیٹس بار غیر فعال کرنے یا سسٹم آئیکنز شامل کرنے اور ہٹانے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"بطور اسٹیٹس بار کام لیں"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ایپ کو کیمرے سے تصویریں لینے اور ویڈیوز بنانے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو آپ کی تصدیق کے بغیر کسی بھی وقت کیمرا استعمال کرنے کی اجازت دیتی ہے۔"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ارتعاش کو کنٹرول کریں"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"ایپ کو وائبریٹر کنٹرول کرنے کی اجازت دیتا ہے۔"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"فلیش لائٹ کنٹرول"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"ایپ کو فلیش لائٹ کنٹرول کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"براہ راست فون نمبرز پر کال کریں"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"ایپ کو آپ کی مداخلت کے بغیر فون نمبروں پر کال کرنے کی اجازت دیتا ہے۔ اس کے نتیجے میں غیر متوقع چارجز یا کالیں ہوسکتی ہیں۔ نوٹ کرلیں کہ یہ ایپ کو ہنگامی نمبروں پر کال کرنے کی اجازت نہیں دیتا ہے۔ نقصان دہ ایپس آپ کی تصدیق کے بغیر کالیں کرکے آپ کی رقم صرف کروا سکتے ہیں۔"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"‏IMS کال سروس تک رسائی حاصل کریں"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 3f3d151..c4be0b9 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bunga kredit karta raqamlari va parollar kabi shaxsiy ma’lumotlar kiradi."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekranni kattalashtirishni boshqarish"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekranni kattalashtirish darajasi va joylashuvini boshqaradi."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Imo-ishoralar bilan boshqarish"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Bosish, surish; jipslashtirish va boshqa imo-ishoralarni amalga oshirish mumkin."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"holat panelini o‘zgartirish yoki o‘chirish"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Ilova holat panelini o‘chirib qo‘yishi hamda tizim ikonkalarini qo‘shishi yoki olib tashlashi mumkin."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"holat qatorida ko‘rinishi"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ilovaga kameradan foydalanib rasm va videoga olishga ruxsat beradi. Bu ruxsat ilovaga sizdan tasdiqlashni so‘ramasdan kameradan foydalanishga imkon beradi."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"tebranishni boshqarish"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Ilova tebranishli signallarni boshqarishi mumkin."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"chiroq chaqnashini boshqarish"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Ilova chaqnoqni boshqarishi mumkin."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"telefon raqamlariga tog‘ridan to‘g‘ri qo‘ng‘iroq qilish"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Ilovaga sizning yordamingizsiz telefonga qo‘ng‘iroq qilish imkonini beradi. Bu kutilmagan qo‘ng‘iroqlarni amalga oshirishi yoki ortiqcha to‘lovlarni yuzaga keltirishi mumkin. Shunga e’tibor qilinki, u favqulodda telefon raqamlariga qo‘ng‘iroqlar qilishga ruxsat bermaydi. Zararli ilovalar sizdan so‘ramasdan qo‘ng‘iroqlarni amalga oshirib, pulingizni sarflashi mumkin."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS qo‘ng‘iroq xizmatiga kirish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index c538684..69b0b99 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bao gồm dữ liệu cá nhân chẳng hạn như số thẻ tín dụng và mật khẩu."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kiểm soát thu phóng màn hình"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kiểm soát vị trí và mức thu phóng của màn hình."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Thực hiện cử chỉ"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Có thể nhấn, vuốt, chụm và thực hiện các cử chỉ khác."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"vô hiệu hóa hoặc sửa đổi thanh trạng thái"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Cho phép ứng dụng vô hiệu hóa thanh trạng thái hoặc thêm và xóa biểu tượng hệ thống."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"trở thành thanh trạng thái"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Cho phép ứng dụng chụp ảnh và quay video bằng máy ảnh. Quyền này cho phép ứng dụng sử dụng máy ảnh bất kỳ lúc nào mà không cần sự xác nhận của bạn."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"kiểm soát rung"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Cho phép ứng dụng kiểm soát bộ rung."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"kiểm soát đèn nháy"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Cho phép ứng dụng kiểm soát đèn nháy."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"gọi trực tiếp số điện thoại"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Cho phép ứng dụng gọi các số điện thoại mà không cần sự can thiệp của bạn. Việc này có thể dẫn đến các khoản phí hoặc cuộc gọi không mong muốn. Lưu ý rằng quyền này không cho phép ứng dụng gọi các số khẩn cấp. Các ứng dụng độc hại có thể khiến bạn tốn tiền do thực hiện cuộc gọi mà không cần sự xác nhận của bạn."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"truy cập dịch vụ gọi điện qua IMS"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 77053f2..4c7bfd3 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包含个人数据,例如信用卡号和密码。"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控制显示内容放大功能"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控制显示内容的缩放级别和位置。"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"执行手势"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"可执行点按、滑动、双指张合等手势。"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改状态栏"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用停用状态栏或者增删系统图标。"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"用作状态栏"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"允许该应用使用相机拍摄照片和视频。此权限可让该应用随时使用相机,而无需您的确认。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"控制振动"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"允许应用控制振动器。"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"控制闪光灯"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"允许应用控制闪光灯。"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"直接拨打电话号码"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"允许该应用在您未执行操作的情况下拨打电话号码。此权限可能会导致意外收费或呼叫。请注意,此权限不允许该应用拨打紧急电话号码。恶意应用可通过拨打电话产生相关费用,而无需您的确认。"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"使用即时通讯通话服务"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 612efe4..d6cacb7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控制顯示屏的放大功能"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控制顯示屏的縮放程度和位置。"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"執行手勢"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"可以輕按、快速滑動和兩指縮放,並執行其他手勢。"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改狀態列"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"成為狀態列"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"允許應用程式使用相機拍照和錄影。這項權限允許應用程式隨時使用相機,而不需經您確認。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"控制震動"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"允許應用程式控制震動。"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"控制閃光燈"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"允許應用程式控制閃光燈。"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"直接撥打電話號碼"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"允許應用程式繞過您自行撥打電話號碼,但可能會產生未預期的費用或撥打未預期的電話。注意:這項權限不允許應用程式撥打緊急電話。惡意應用程式可能未經您確認擅自撥打電話,增加您的支出。"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"使用 IMS 通話服務"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 201cc1e..a47f73d 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控管顯示畫面放大功能"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控管顯示畫面的縮放等級和位置。"</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"使用手勢"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"可使用輕按、滑動和雙指撥動等手勢。"</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"以狀態列顯示"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"允許應用程式使用相機拍照和錄影。這項權限可讓應用程式隨時使用相機,而不需請求您進行確認。"</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"控制震動"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"允許應用程式控制震動。"</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"控制閃光燈"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"允許應用程式控制閃光燈。"</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"直接撥打電話號碼"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"允許應用程式自行撥打電話,但可能產生非預期的費用或撥打非預期的電話。注意:這項權限不允許應用程式撥打緊急電話。惡意應用程式可能利用此功能擅自撥打電話,增加您不必要的額外支出。"</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"存取 IMS 撥號服務"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5e835e3..b34594f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -256,6 +256,8 @@
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kufaka phakathi idatha yomuntu siqu efana nezinombolo zekhadi lesikweletu namaphasiwedi."</string>
     <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Lawula ukulungiswa kwesibonisi"</string>
     <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Lawula ileveli yokusondeza yesibonisi nendawo."</string>
+    <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Yenza ukuthinta"</string>
+    <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Ingathepha, iswayiphe, incinze, futhi yenze okunye ukuthintwa."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"khubaza noma guqula ibha yomumo"</string>
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Ivumela uhlelo lokusebenza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo."</string>
     <string name="permlab_statusBarService" msgid="4826835508226139688">"yiba yibha yesimo"</string>
@@ -354,8 +356,6 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ivumela uhlelo lokusebenza ukuthatha izithombe namavidiyo ngekhamera. Le mvume ivumela uhlelo lokusebenza ukusebenzisa ikhamera nganoma isiphi isikhathi ngaphandle kwemvume yakho."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"lawula ukudlidliza"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Ivumela uhlelo lokusebenza ukulawula isidlidlizi."</string>
-    <string name="permlab_flashlight" msgid="2155920810121984215">"lawula ukukhanya kwefulashi"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Ivumela uhlelo lokusebenza ukulawula ukukhanya kwefuleshi."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"ngokuqondile shayela izinombolo zocingo"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Ivumela uhlelo lokusebenza ukushayela izinombolo zefoni ngaphandle kokuhlanganyela kwakho. Lokhu kungaholela emashajini noma amakholi angalindelekile. Qaphela ukuthi lokhu akuvumeli uhlelo lokusebenza ukushayela izinombolo zesimo esiphuthumayo. Izinhlelo zokusebenza ezingalungile zingabiza imali ngokwenze amakholi ngaphandle kokuqinisekisa kwakho."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"finyelela kusevisi yekholi ye-IMS"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9f13565..e0f9eca 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3316,7 +3316,14 @@
              </p>
          -->
         <attr name="canControlMagnification" format="boolean" />
-        <!-- Short description of the accessibility serivce purpose or behavior.-->
+        <!-- Attribute whether the accessibility service wants to be able to perform gestures.
+             <p>
+             Required to allow setting the {@link android.accessibilityservice
+             #AccessibilityServiceInfo#FLAG_CAN_PERFORM_GESTURES} flag.
+             </p>
+         -->
+        <attr name="canPerformGestures" format="boolean" />
+        <!-- Short description of the accessibility service purpose or behavior.-->
         <attr name="description" />
     </declare-styleable>
 
@@ -4039,9 +4046,9 @@
              should always be false for Material and  beyond.
              @hide Developers shouldn't need to change this. -->
         <attr name="useDisabledAlpha" format="boolean" />
-        <!-- Tint to apply to the button graphic. -->
+        <!-- Tint to apply to the thumb drawable. -->
         <attr name="thumbTint" format="color" />
-        <!-- Blending mode used to apply the button graphic tint. -->
+        <!-- Blending mode used to apply the thumb tint. -->
         <attr name="thumbTintMode">
             <!-- The tint is drawn on top of the drawable.
                  [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
@@ -4061,6 +4068,30 @@
                  result to valid color values. Saturate(S + D) -->
             <enum name="add" value="16" />
         </attr>
+        <!-- Drawable displayed at each progress position on a seekbar. -->
+        <attr name="tickMark" format="reference" />
+        <!-- Tint to apply to the tick mark drawable. -->
+        <attr name="tickMarkTint" format="color" />
+        <!-- Blending mode used to apply the tick mark tint. -->
+        <attr name="tickMarkTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="StackView">
@@ -7836,6 +7867,12 @@
         <attr name="label" />
         <!-- The key character map file resource. -->
         <attr name="keyboardLayout" format="reference" />
+        <!-- The locales the given keyboard layout corresponds to. -->
+        <attr name="locale" format="string" />
+        <!-- The vendor ID of the hardware the given layout corresponds to. @hide -->
+        <attr name="vendorId" format="integer" />
+        <!-- The product ID of the hardware the given layout corresponds to. @hide -->
+        <attr name="productId" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="MediaRouteButton">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 2a11081..6eba78a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1748,6 +1748,10 @@
         <attr name="isolatedProcess" format="boolean" />
         <attr name="singleUser" />
         <attr name="encryptionAware" />
+        <!-- If the service is an {@link android.R.attr#isolatedProcess} service, this permits a
+             client to bind to the service as if it were running it its own package.  The service
+             must also be {@link android.R.attr#exported} if this flag is set. -->
+        <attr name="externalService" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>receiver</code> tag declares an
@@ -1857,6 +1861,11 @@
         <attr name="lockTaskMode" />
         <attr name="showForAllUsers" />
         <attr name="encryptionAware" />
+        <!-- @hide This activity is always focusable regardless of if it is in a task/stack whose
+             activities are normally not focusable.
+             For example, {@link android.R.attr#supportsPictureInPicture} activities are placed
+             in a task/stack that isn't focusable. This flag allows them to be focusable.-->
+        <attr name="alwaysFocusable" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 74a5f4e..53a733d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -413,22 +413,29 @@
     <!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
     <bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">true</bool>
 
+    <!-- Boolean indicating whether or not wifi should turn off when emergency call is made -->
+    <bool translatable="false" name="config_wifi_turn_off_during_emergency_call">false</bool>
+
     <!-- Integer specifying the basic autojoin parameters -->
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
-    <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">5</integer>
+    <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">40</integer>
     <integer translatable="false" name="config_wifi_framework_current_association_hysteresis_high">16</integer>
     <integer translatable="false" name="config_wifi_framework_current_association_hysteresis_low">10</integer>
     <integer translatable="false" name="config_wifi_framework_5GHz_preference_penalty_threshold">-75</integer>
-    <integer translatable="false" name="config_wifi_framework_5GHz_preference_penalty_factor">2</integer>
-
+    <integer translatable="false" name="config_wifi_framework_RSSI_SCORE_OFFSET">85</integer>
+    <integer translatable="false" name="config_wifi_framework_RSSI_SCORE_SLOPE">4</integer>
+    <integer translatable="false" name="config_wifi_framework_SAME_BSSID_AWARD">24</integer>
+    <integer translatable="false" name="config_wifi_framework_LAST_SELECTION_AWARD">480</integer>
+    <integer translatable="false" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD">40</integer>
+    <integer translatable="false" name="config_wifi_framework_SECURITY_AWARD">80</integer>
     <!-- Integer parameters of the wifi to cellular handover feature
          wifi should not stick to bad networks -->
     <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-82</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-72</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-60</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-87</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-77</integer>
-    <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-65</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-70</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-57</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-85</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-73</integer>
+    <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-60</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_24">6</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_5">12</integer>
     <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_24">24</integer>
@@ -502,7 +509,7 @@
     <integer translatable="false" name="config_wifi_framework_network_black_list_min_time_milli">120000</integer>
 
     <!-- Integer indicating RSSI boost given to current network -->
-    <integer translatable="false" name="config_wifi_framework_current_network_boost">25</integer>
+    <integer translatable="false" name="config_wifi_framework_current_network_boost">16</integer>
 
     <!-- Integer indicating how to handle beacons with uninitialized RSSI value of 0 -->
     <integer translatable="false" name="config_wifi_framework_scan_result_rssi_level_patchup_value">-85</integer>
@@ -974,6 +981,7 @@
             0 - Nothing
             1 - Recent apps view in SystemUI
             2 - Launch assist intent
+            3 - Start picture-in-picture (PIP) or launch PIP UI
          This needs to match the constants in
          policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
     -->
@@ -2413,4 +2421,25 @@
 
     <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
     <string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
+
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
+         is located in center. -->
+    <string translatable="false" name="config_centeredPictureInPictureBounds">"0 0 300 300"</string>
+
+    <!-- Controls the snap mode for the docked stack divider
+             0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
+             1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
+             2 - 1 snap target: 1:1
+    -->
+    <integer name="config_dockedStackDividerSnapMode">0</integer>
+
+    <!-- List of comma separated package names for which we the system will not show crash, ANR,
+         etc. dialogs. -->
+    <string translatable="false" name="config_appsNotReportingCrashes"></string>
+
+    <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
+         the device to be "idle" after being inactive for this long. -->
+    <integer name="config_jobSchedulerInactivityIdleThreshold">4260000</integer>
+    <!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
+    <integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index ef6b467..b2482cd 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -35,6 +35,13 @@
     <dimen name="navigation_bar_height_landscape">48dp</dimen>
     <!-- Width of the navigation bar when it is placed vertically on the screen -->
     <dimen name="navigation_bar_width">48dp</dimen>
+    <!-- Height of the bottom navigation / system bar in car mode. -->
+    <dimen name="navigation_bar_height_car_mode">96dp</dimen>
+    <!-- Height of the bottom navigation bar in portrait; often the same as
+         @dimen/navigation_bar_height_car_mode -->
+    <dimen name="navigation_bar_height_landscape_car_mode">96dp</dimen>
+    <!-- Width of the navigation bar when it is placed vertically on the screen in car mode -->
+    <dimen name="navigation_bar_width_car_mode">96dp</dimen>
     <!-- Height of notification icons in the status bar -->
     <dimen name="status_bar_icon_size">24dip</dimen>
     <!-- Size of the giant number (unread count) in the notifications -->
@@ -429,4 +436,6 @@
 
     <item type="dimen" format="integer" name="time_picker_column_start_material">0</item>
     <item type="dimen" format="integer" name="time_picker_column_end_material">1</item>
+
+    <item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item>
 </resources>
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index eee5771..3cfd9f4 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -49,6 +49,7 @@
         <item>ar-SY</item> <!-- Arabic (Syria) -->
         <item>ar-TD</item> <!-- Arabic (Chad) -->
         <item>ar-TN</item> <!-- Arabic (Tunisia) -->
+        <item>ar-XB</item> <!-- Right-to-left pseudolocale -->
         <item>ar-YE</item> <!-- Arabic (Yemen) -->
         <item>as-IN</item> <!-- Assamese (India) -->
         <item>asa-TZ</item> <!-- Asu (Tanzania) -->
@@ -195,6 +196,7 @@
         <item>en-VI</item> <!-- English (U.S. Virgin Islands) -->
         <item>en-VU</item> <!-- English (Vanuatu) -->
         <item>en-WS</item> <!-- English (Samoa) -->
+        <item>en-XA</item> <!-- Left-to-right pseudolocale -->
         <item>en-ZA</item> <!-- English (South Africa) -->
         <item>en-ZM</item> <!-- English (Zambia) -->
         <item>en-ZW</item> <!-- English (Zimbabwe) -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 09c1717..c883b1f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2685,8 +2685,14 @@
     <public type="attr" name="canControlMagnification" />
     <public type="attr" name="languageTag" />
     <public type="attr" name="pointerShape" />
+    <public type="attr" name="tickMark" />
+    <public type="attr" name="tickMarkTint" />
+    <public type="attr" name="tickMarkTintMode" />
+    <public type="attr" name="canPerformGestures" />
+    <public type="attr" name="externalService" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
+    <public type="style" name="Widget.Material.SeekBar.Discrete" />
 
     <public type="id" name="accessibilityActionSetProgress" />
     <public type="id" name="icon_frame" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 347f2f9..4ef3f59 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -652,6 +652,12 @@
     <string name="capability_desc_canControlMagnification">Control the display\'s zoom level and
         positioning.</string>
 
+    <!-- Title for the capability of an accessibility service to perform gestures. -->
+    <string name="capability_title_canPerformGestures">Perform gestures</string>
+    <!-- Description for the capability of an accessibility service to perform gestures. -->
+    <string name="capability_desc_canPerformGestures">Can tap, swipe, pinch, and perform other
+        gestures.</string>
+
     <!--  Permissions -->
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1026,11 +1032,6 @@
     <string name="permdesc_vibrate">Allows the app to control the vibrator.</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_flashlight">control flashlight</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_flashlight">Allows the app to control the flashlight.</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_callPhone">directly call phone numbers</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_callPhone">Allows the app to call phone numbers
@@ -4128,4 +4129,13 @@
 
     <string name="importance_from_topic">You set the importance of these notifications.</string>
     <string name="importance_from_person">This is important because of the people involved.</string>
+
+    <!-- Message to user that app trying to create user is not allowed to due to restrictions. [CHAR LIMIT=none] -->
+    <string name="user_creation_cannot_add"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user, but is currently prohibited.</string>
+    <!-- Message to user that app trying to create user is not allowed to due to user limit being reached. [CHAR LIMIT=none] -->
+    <string name="user_creation_cannot_add_any_more"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user, but the user limit has been reached.</string>
+    <!-- Message to user that app trying to create user for an account that already exists. [CHAR LIMIT=none] -->
+    <string name="user_creation_account_exists"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user, but the account <b><xliff:g id="account" example="foobar">%2$s</xliff:g></b> already exists on this device. Proceed anyway?</string>
+    <!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
+    <string name="user_creation_adding"><b><xliff:g id="app" example="Gmail">%1$s</xliff:g></b> is trying to add a new user for the account <b><xliff:g id="account" example="foobar">%2$s</xliff:g></b>. Proceed?</string>
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 8485e59..3d5f6ab 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -745,6 +745,11 @@
         <item name="background">@drawable/control_background_32dp_material</item>
     </style>
 
+    <!-- A seek bar with tick marks at each progress value. -->
+    <style name="Widget.Material.SeekBar.Discrete">
+        <item name="tickMark">@drawable/seekbar_tick_mark_material</item>
+    </style>
+
     <style name="Widget.Material.RatingBar" parent="Widget.RatingBar">
         <item name="progressDrawable">@drawable/ratingbar_material</item>
         <item name="indeterminateDrawable">@drawable/ratingbar_material</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 39aad17..dc288d5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -300,18 +300,25 @@
   <java-symbol type="bool" name="config_wifi_enable_5GHz_preference" />
   <java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
   <java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
+  <java-symbol type="bool" name="config_wifi_turn_off_during_emergency_call" />
   <java-symbol type="bool" name="config_supportMicNearUltrasound" />
   <java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
   <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" />
   <java-symbol type="bool" name="config_freeformWindowManagement" />
   <java-symbol type="string" name="config_defaultPictureInPictureBounds" />
+  <java-symbol type="string" name="config_centeredPictureInPictureBounds" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_high" />
   <java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_low" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
-  <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_factor" />
+  <java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_OFFSET" />
+  <java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_SLOPE" />
+  <java-symbol type="integer" name="config_wifi_framework_SAME_BSSID_AWARD" />
+  <java-symbol type="integer" name="config_wifi_framework_LAST_SELECTION_AWARD" />
+  <java-symbol type="integer" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD" />
+  <java-symbol type="integer" name="config_wifi_framework_SECURITY_AWARD" />
   <java-symbol type="integer" name="config_wifi_disconnected_short_scan_interval" />
   <java-symbol type="integer" name="config_wifi_disconnected_long_scan_interval" />
   <java-symbol type="integer" name="config_wifi_associated_short_scan_interval" />
@@ -562,6 +569,8 @@
   <java-symbol type="string" name="capability_title_canRetrieveWindowContent" />
   <java-symbol type="string" name="capability_desc_canControlMagnification" />
   <java-symbol type="string" name="capability_title_canControlMagnification" />
+  <java-symbol type="string" name="capability_desc_canPerformGestures" />
+  <java-symbol type="string" name="capability_title_canPerformGestures" />
   <java-symbol type="string" name="cfTemplateForwarded" />
   <java-symbol type="string" name="cfTemplateForwardedTime" />
   <java-symbol type="string" name="cfTemplateNotForwarded" />
@@ -576,6 +585,7 @@
   <java-symbol type="string" name="config_ntpServer" />
   <java-symbol type="string" name="config_useragentprofile_url" />
   <java-symbol type="string" name="config_wifi_p2p_device_type" />
+  <java-symbol type="string" name="config_appsNotReportingCrashes" />
   <java-symbol type="string" name="contentServiceSync" />
   <java-symbol type="string" name="contentServiceSyncNotificationTitle" />
   <java-symbol type="string" name="contentServiceTooManyDeletesNotificationDesc" />
@@ -928,6 +938,10 @@
   <java-symbol type="string" name="time_picker_increment_minute_button" />
   <java-symbol type="string" name="time_picker_increment_set_pm_button" />
   <java-symbol type="string" name="upload_file" />
+  <java-symbol type="string" name="user_creation_cannot_add" />
+  <java-symbol type="string" name="user_creation_cannot_add_any_more" />
+  <java-symbol type="string" name="user_creation_account_exists" />
+  <java-symbol type="string" name="user_creation_adding" />
   <java-symbol type="string" name="user_switched" />
   <java-symbol type="string" name="user_switching_message" />
   <java-symbol type="string" name="user_logging_out_message" />
@@ -1493,9 +1507,14 @@
   <java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
   <java-symbol type="dimen" name="docked_stack_divider_thickness" />
   <java-symbol type="dimen" name="docked_stack_divider_insets" />
+  <java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
+  <java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
   <java-symbol type="dimen" name="navigation_bar_height" />
   <java-symbol type="dimen" name="navigation_bar_height_landscape" />
   <java-symbol type="dimen" name="navigation_bar_width" />
+  <java-symbol type="dimen" name="navigation_bar_height_car_mode" />
+  <java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" />
+  <java-symbol type="dimen" name="navigation_bar_width_car_mode" />
   <java-symbol type="dimen" name="status_bar_height" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_off" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_on" />
@@ -2245,6 +2264,9 @@
 
   <java-symbol type="integer" name="config_defaultNightMode" />
 
+  <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
+  <java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
+
   <java-symbol type="style" name="Animation.ImmersiveModeConfirmation" />
 
   <java-symbol type="integer" name="config_screen_magnification_multi_tap_adjustment" />
diff --git a/core/res/res/xml/config_webview_packages.xml b/core/res/res/xml/config_webview_packages.xml
index fd443c1..f062b59 100644
--- a/core/res/res/xml/config_webview_packages.xml
+++ b/core/res/res/xml/config_webview_packages.xml
@@ -16,6 +16,6 @@
 
 <webviewproviders>
     <!-- The default WebView implementation -->
-    <webviewprovider description="Android WebView" packageName="com.android.webview">
+    <webviewprovider description="Android WebView" packageName="com.android.webview" availableByDefault="true">
     </webviewprovider>
 </webviewproviders>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 5f1f927..77d2ada 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -33,6 +33,9 @@
          and http://mobilcent.com/info-worldwide.asp and extracted from:
          http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
 
+    <!-- Arab Emirates -->
+    <shortcode country="ae" free="3214|1017" />
+
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
 
@@ -43,7 +46,7 @@
     <shortcode country="at" pattern="11\\d{4}" premium="09.*" free="116\\d{3}" />
 
     <!-- Australia: 6 or 8 digits starting with "19" -->
-    <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882" />
+    <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882|19944444" />
 
     <!-- Azerbaijan: 4-5 digits, known premium codes listed -->
     <shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
@@ -58,10 +61,10 @@
     <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
 
     <!-- Canada: 5-6 digits -->
-    <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188" />
+    <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" />
 
     <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
-    <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111" />
+    <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" />
 
     <!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
          http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
@@ -75,7 +78,7 @@
     <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
 
     <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
-    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215" />
+    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529" />
 
     <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
@@ -89,7 +92,7 @@
     <shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}" />
 
     <!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
-    <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}" />
+    <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}|14789" />
 
     <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
          http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements,
@@ -122,9 +125,15 @@
          http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
     <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}" />
 
+    <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
+    <shortcode country="jp" free="8083" />
+
     <!-- Kyrgyzstan: 4 digits, known premium codes listed -->
     <shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
 
+    <!-- Korea: http://www.smsideatechnosolutions.com/chhattisgarh/korea/sms-short-code.html -->
+    <shortcode country="kr" pattern="\\d{4,7}" free="\\*9712|\\*9090|##900" />
+
     <!-- Kazakhstan: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-kazakhstan/ -->
     <shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
 
@@ -151,27 +160,33 @@
     <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" />
 
     <!-- New Zealand: 3-4 digits, known premium codes listed -->
-    <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995" />
+    <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" />
+
+    <!-- Philippines -->
+    <shortcode country="ph" free="2147" />
 
     <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
-    <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}" />
+    <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012" />
 
     <!-- Portugal: 5 digits, plus EU:
          http://clients.txtnation.com/entries/158326-portugal-premium-sms-short-code-regulations -->
     <shortcode country="pt" premium="6[1289]\\d{3}" free="116\\d{3}" />
 
     <!-- Romania: 4 digits, plus EU: http://www.simplus.ro/en/resources/glossary-of-terms/ -->
-    <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}" />
+    <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}|3654" />
 
     <!-- Russia: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-russia/ -->
-    <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)" />
+    <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424" />
+
+    <!-- Saudi Arabia -->
+    <shortcode country="sa" free="8145" />
 
     <!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
     <shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
 
     <!-- Singapore: 5 digits: http://clients.txtnation.com/entries/306442-singapore-premium-sms-short-code-requirements
          Free government directory info at 74688: http://app.sgdi.gov.sg/sms_help.asp -->
-    <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688" />
+    <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688|70134" />
 
     <!-- Slovenia: 4 digits (premium=3xxx, 6xxx, 8xxx), plus EU: http://www.cmtelecom.com/premium-sms/slovenia -->
     <shortcode country="si" pattern="\\d{4}" premium="[368]\\d{3}" free="116\\d{3}" />
@@ -179,9 +194,15 @@
     <!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
     <shortcode country="sk" premium="\\d{4}" free="116\\d{3}" />
 
+    <!-- Thailand: 4186001 used by AIS_TH_DCB -->
+    <shortcode country="th" free="4186001" />
+
     <!-- Tajikistan: 4 digits, known premium codes listed -->
     <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
 
+    <!-- Turkey -->
+    <shortcode country="tr" free="7529|5528" />
+
     <!-- Ukraine: 4 digits, known premium codes listed -->
     <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
 
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index 6bd8f6e..a391e1f 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -75,6 +75,7 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager -->
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 7cd25af..ee8921e 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -22,7 +22,7 @@
 	$(call all-java-files-under, EnabledTestApp/src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_AAPT_FLAGS = -0 dat -0 gld
+LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
 LOCAL_STATIC_JAVA_LIBRARIES := \
     core-tests-support \
     android-common \
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 5177836..c0453f8 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -146,6 +146,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.DatePickerActivity"
+                android:label="DatePickerActivity"
+                android:screenOrientation="portrait"
+                android:theme="@android:style/Theme.Material.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.focus.DescendantFocusability" android:label="DescendantFocusability">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/res/layout/datepicker_layout.xml b/core/tests/coretests/res/layout/datepicker_layout.xml
new file mode 100644
index 0000000..a79f87d
--- /dev/null
+++ b/core/tests/coretests/res/layout/datepicker_layout.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    <DatePicker
+            android:id="@+id/datePicker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"/>
+    <EditText
+            android:id="@+id/belowPicker"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="Some Text"
+            android:textAlignment="center"
+            android:layout_below="@id/datePicker"/>
+</RelativeLayout>
diff --git a/core/tests/coretests/res/values-fa/strings.xml b/core/tests/coretests/res/values-fa/strings.xml
new file mode 100644
index 0000000..c83f5f1
--- /dev/null
+++ b/core/tests/coretests/res/values-fa/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="dummy_string">رشتهٔ الکی</string>
+</resources>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 04b0478..ef915bb 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -139,4 +139,7 @@
     <!-- RestrictionsManagerTest -->
     <string name="restrictionManager_title">Title</string>
     <string name="restrictionManager_desc">Description</string>
+
+    <!-- ResourcesLocaleResolutionTest -->
+    <string name="dummy_string">dummy string</string>
 </resources>
diff --git a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java b/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java
deleted file mode 100644
index 37495e1..0000000
--- a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.os.Parcel;
-import android.test.AndroidTestCase;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-
-public class ManifestDigestTest extends AndroidTestCase {
-    private static final byte[] MESSAGE_1 = {
-            (byte) 0x00, (byte) 0xAA, (byte) 0x55, (byte) 0xFF
-    };
-
-    public void testManifestDigest_FromInputStream_Null() {
-        assertNull("Attributes were null, so ManifestDigest.fromAttributes should return null",
-                ManifestDigest.fromInputStream(null));
-    }
-
-    public void testManifestDigest_FromInputStream_ThrowsIoException() {
-        InputStream is = new InputStream() {
-            @Override
-            public int read() throws IOException {
-                throw new IOException();
-            }
-        };
-
-        assertNull("InputStream threw exception, so ManifestDigest should be null",
-                ManifestDigest.fromInputStream(is));
-    }
-
-    public void testManifestDigest_Equals() throws Exception {
-        InputStream is = new ByteArrayInputStream(MESSAGE_1);
-
-        ManifestDigest expected =
-                new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1));
-
-        ManifestDigest actual = ManifestDigest.fromInputStream(is);
-        assertEquals(expected, actual);
-
-        ManifestDigest unexpected = new ManifestDigest(new byte[0]);
-        assertFalse(unexpected.equals(actual));
-    }
-
-    public void testManifestDigest_Parcel() throws Exception {
-        InputStream is = new ByteArrayInputStream(MESSAGE_1);
-
-        ManifestDigest digest = ManifestDigest.fromInputStream(is);
-
-        Parcel p = Parcel.obtain();
-        digest.writeToParcel(p, 0);
-        p.setDataPosition(0);
-
-        ManifestDigest fromParcel = ManifestDigest.CREATOR.createFromParcel(p);
-
-        assertEquals("ManifestDigest going through parceling should be the same as before: "
-                + digest.toString() + " and " + fromParcel.toString(), digest,
-                fromParcel);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
index 9b216cb..d963812 100644
--- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
@@ -16,7 +16,6 @@
 
 package android.content.pm;
 
-import android.content.pm.ManifestDigest;
 import android.content.pm.VerificationParams;
 import android.net.Uri;
 import android.os.Parcel;
@@ -33,7 +32,6 @@
     private final static String VERIFICATION_URI_STRING = "http://verification.uri/path";
     private final static String ORIGINATING_URI_STRING = "http://originating.uri/path";
     private final static String REFERRER_STRING = "http://referrer.uri/path";
-    private final static byte[] DIGEST_BYTES = "fake digest".getBytes();
     private final static int INSTALLER_UID = 42;
 
     private final static Uri VERIFICATION_URI = Uri.parse(VERIFICATION_URI_STRING);
@@ -42,11 +40,9 @@
 
     private final static int ORIGINATING_UID = 10042;
 
-    private final static ManifestDigest MANIFEST_DIGEST = new ManifestDigest(DIGEST_BYTES);
-
     public void testParcel() throws Exception {
         VerificationParams expected = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         Parcel parcel = Parcel.obtain();
         expected.writeToParcel(parcel, 0);
@@ -61,85 +57,70 @@
         assertEquals(REFERRER, actual.getReferrer());
 
         assertEquals(ORIGINATING_UID, actual.getOriginatingUid());
-
-        assertEquals(MANIFEST_DIGEST, actual.getManifestDigest());
     }
 
     public void testEquals_Success() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
 
         assertEquals(params1, params2);
     }
 
     public void testEquals_VerificationUri_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse("http://a.different.uri/"), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
 
         assertFalse(params1.equals(params2));
     }
 
     public void testEquals_OriginatingUri_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
 
         assertFalse(params1.equals(params2));
     }
 
     public void testEquals_Referrer_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse("http://a.different.uri/"), ORIGINATING_UID,
-                new ManifestDigest(DIGEST_BYTES));
+                Uri.parse("http://a.different.uri/"), ORIGINATING_UID);
 
         assertFalse(params1.equals(params2));
     }
 
     public void testEquals_Originating_Uid_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), 12345, new ManifestDigest(DIGEST_BYTES));
-
-        assertFalse(params1.equals(params2));
-    }
-
-    public void testEquals_ManifestDigest_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID,
-                new ManifestDigest("a different digest".getBytes()));
+                Uri.parse(REFERRER_STRING), 12345);
 
         assertFalse(params1.equals(params2));
     }
 
     public void testEquals_InstallerUid_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
         params2.setInstallerUid(INSTALLER_UID);
 
         assertFalse(params1.equals(params2));
@@ -147,78 +128,65 @@
 
     public void testHashCode_Success() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
 
         assertEquals(params1.hashCode(), params2.hashCode());
     }
 
     public void testHashCode_VerificationUri_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(null, Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
 
         assertFalse(params1.hashCode() == params2.hashCode());
     }
 
     public void testHashCode_OriginatingUri_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
 
         assertFalse(params1.hashCode() == params2.hashCode());
     }
 
     public void testHashCode_Referrer_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), null,
-                ORIGINATING_UID, new ManifestDigest(DIGEST_BYTES));
+                ORIGINATING_UID);
 
         assertFalse(params1.hashCode() == params2.hashCode());
     }
 
     public void testHashCode_Originating_Uid_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), 12345, new ManifestDigest(DIGEST_BYTES));
-
-        assertFalse(params1.hashCode() == params2.hashCode());
-    }
-
-    public void testHashCode_ManifestDigest_Failure() throws Exception {
-        VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
-
-        VerificationParams params2 = new VerificationParams(
-                Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID,
-                new ManifestDigest("a different digest".getBytes()));
+                Uri.parse(REFERRER_STRING), 12345);
 
         assertFalse(params1.hashCode() == params2.hashCode());
     }
 
     public void testHashCode_InstallerUid_Failure() throws Exception {
         VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
-                REFERRER, ORIGINATING_UID, MANIFEST_DIGEST);
+                REFERRER, ORIGINATING_UID);
 
         VerificationParams params2 = new VerificationParams(
                 Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
-                Uri.parse(REFERRER_STRING), ORIGINATING_UID,
-                new ManifestDigest("a different digest".getBytes()));
+                Uri.parse(REFERRER_STRING), ORIGINATING_UID);
         params2.setInstallerUid(INSTALLER_UID);
 
         assertFalse(params1.hashCode() == params2.hashCode());
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java
new file mode 100644
index 0000000..55c0031
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java
@@ -0,0 +1,53 @@
+/*
+* Copyright (C) 2015 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.content.res;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleResolutionTest extends AndroidTestCase {
+    @SmallTest
+    public void testGetResolvedLocale_englishIsAlwaysConsideredSupported() {
+        // First make sure English has no explicit assets other than the default assets
+        final AssetManager assets = getContext().getAssets();
+        final String supportedLocales[] = assets.getNonSystemLocales();
+        for (String languageTag : supportedLocales) {
+            if ("en-XA".equals(languageTag)) {
+                continue;
+            }
+            assertFalse(
+                    "supported locales: " + Arrays.toString(supportedLocales),
+                    "en".equals(Locale.forLanguageTag(languageTag).getLanguage()));
+        }
+
+        final DisplayMetrics dm = new DisplayMetrics();
+        dm.setToDefaults();
+        final Configuration cfg = new Configuration();
+        cfg.setToDefaults();
+        // Avestan and English have no assets, but Persian does.
+        cfg.setLocales(LocaleList.forLanguageTags("ae,en,fa"));
+        Resources res = new Resources(assets, dm, cfg);
+        // We should get English, because it is always considered supported.
+        assertEquals("en", res.getResolvedLocale().toLanguageTag());
+    }
+}
+
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
new file mode 100644
index 0000000..2e0e6dc
--- /dev/null
+++ b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.text;
+
+import android.text.Layout.Alignment;
+import junit.framework.TestCase;
+
+/**
+ * Tests for text measuring methods of StaticLayout.
+ */
+public class StaticLayoutTextMeasuringTest extends TestCase {
+    private static final float SPACE_MULTI = 1.0f;
+    private static final float SPACE_ADD = 0.0f;
+    private static final int DEFAULT_OUTER_WIDTH = 150;
+    private static final Alignment DEFAULT_ALIGN = Alignment.ALIGN_LEFT;
+
+    private TextPaint mDefaultPaint;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (mDefaultPaint == null) {
+            mDefaultPaint = new TextPaint();
+        }
+    }
+
+    public void testGetPrimaryHorizontal_zwnbsp() {
+        // a, ZERO WIDTH NO-BREAK SPACE
+        String testString = "a\uFEFF";
+        StaticLayout layout = new StaticLayout(testString, mDefaultPaint,
+                DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+
+        assertEquals(0.0f, layout.getPrimaryHorizontal(0));
+        assertEquals(layout.getPrimaryHorizontal(2), layout.getPrimaryHorizontal(1));
+    }
+
+    public void testGetPrimaryHorizontal_devanagari() {
+        // DEVANAGARI LETTER KA, DEVANAGARI VOWEL SIGN AA
+        String testString = "\u0915\u093E";
+        StaticLayout layout = new StaticLayout(testString, mDefaultPaint,
+                DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+
+        assertEquals(0.0f, layout.getPrimaryHorizontal(0));
+        assertEquals(layout.getPrimaryHorizontal(2), layout.getPrimaryHorizontal(1));
+    }
+
+    public void testGetPrimaryHorizontal_flagEmoji() {
+        // REGIONAL INDICATOR SYMBOL LETTER U, REGIONAL INDICATOR SYMBOL LETTER S, REGIONAL
+        // INDICATOR SYMBOL LETTER Z
+        // First two code points (U and S) forms a US flag.
+        String testString = "\uD83C\uDDFA\uD83C\uDDF8\uD83C\uDDFF";
+        StaticLayout layout = new StaticLayout(testString, mDefaultPaint,
+                DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+
+        assertEquals(0.0f, layout.getPrimaryHorizontal(0));
+        assertEquals(layout.getPrimaryHorizontal(4), layout.getPrimaryHorizontal(1));
+        assertEquals(layout.getPrimaryHorizontal(4), layout.getPrimaryHorizontal(2));
+        assertEquals(layout.getPrimaryHorizontal(4), layout.getPrimaryHorizontal(3));
+
+        assertTrue(layout.getPrimaryHorizontal(6) > layout.getPrimaryHorizontal(4));
+        assertEquals(layout.getPrimaryHorizontal(6), layout.getPrimaryHorizontal(5));
+    }
+}
diff --git a/core/tests/coretests/src/android/util/LocaleListTest.java b/core/tests/coretests/src/android/util/LocaleListTest.java
new file mode 100644
index 0000000..de1382d
--- /dev/null
+++ b/core/tests/coretests/src/android/util/LocaleListTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LocaleList;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+public class LocaleListTest extends TestCase {
+    @SmallTest
+    public void testConstructor() throws Exception {
+        LocaleList ll;
+        ll = new LocaleList(Locale.forLanguageTag("fr"), null);
+        assertEquals("fr", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.getEmptyLocaleList());
+        assertEquals("fr", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("fr"));
+        assertEquals("fr", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de"));
+        assertEquals("fr,de", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,ja"));
+        assertEquals("fr,de,ja", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,fr,ja"));
+        assertEquals("fr,de,ja", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,fr"));
+        assertEquals("fr,de", ll.toLanguageTags());
+
+        ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("fr,de"));
+        assertEquals("fr,de", ll.toLanguageTags());
+    }
+
+    @SmallTest
+    public void testConstructor_nullThrows() throws Exception {
+        try {
+            final LocaleList ll = new LocaleList(null, LocaleList.getEmptyLocaleList());
+            fail("Constructing with locale and locale list should throw with a null locale.");
+        } catch (Throwable e) {
+            assertEquals(NullPointerException.class, e.getClass());
+        }
+    }
+
+    @SmallTest
+    public void testGetDefault_localeSetDefaultCalledButNoChangeNecessary() throws Exception {
+        final Locale originalLocale = Locale.getDefault();
+        final LocaleList originalLocaleList = LocaleList.getDefault();
+        final int originalLocaleIndex = originalLocaleList.indexOf(originalLocale);
+
+        // This simulates a situation potentially set by the system processes
+        LocaleList.setDefault(LocaleList.forLanguageTags("ae,en,ja"), 1 /* en */);
+
+        // check our assumptions about input
+        assertEquals("en", Locale.getDefault().toLanguageTag());
+        final LocaleList firstResult = LocaleList.getDefault();
+        assertEquals("ae,en,ja", LocaleList.getDefault().toLanguageTags());
+
+        Locale.setDefault(Locale.forLanguageTag("ae"));
+        assertSame(firstResult, LocaleList.getDefault());
+
+        // restore the original values
+        LocaleList.setDefault(originalLocaleList, originalLocaleIndex);
+    }
+}
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 253eb25..d383775 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -17,14 +17,16 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 import android.test.suitebuilder.annotation.Suppress;
-import android.util.Patterns;
 
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import junit.framework.TestCase;
 
 public class PatternsTest extends TestCase {
 
+    //Tests for Patterns.TOP_LEVEL_DOMAIN
+
     @SmallTest
     public void testTldPattern() throws Exception {
         boolean t;
@@ -40,7 +42,7 @@
         t = Patterns.TOP_LEVEL_DOMAIN.matcher("xn--0zwm56d").matches();
         assertTrue("Missed valid TLD", t);
 
-        // One of the new top level internationalized domain.
+        // One of the new top level unicode domain.
         t = Patterns.TOP_LEVEL_DOMAIN.matcher("\uD55C\uAD6D").matches();
         assertTrue("Missed valid TLD", t);
 
@@ -54,60 +56,372 @@
         assertFalse("Matched invalid TLD!", t);
     }
 
+    //Tests for Patterns.IANA_TOP_LEVEL_DOMAINS
+
     @SmallTest
-    @Suppress // Failing.
-    public void testUrlPattern() throws Exception {
-        boolean t;
-
-        t = Patterns.WEB_URL.matcher("http://www.google.com").matches();
-        assertTrue("Valid URL", t);
-
-        // Google in one of the new top level domain.
-        t = Patterns.WEB_URL.matcher("http://www.google.me").matches();
-        assertTrue("Valid URL", t);
-        t = Patterns.WEB_URL.matcher("google.me").matches();
-        assertTrue("Valid URL", t);
-
-        // Test url in Chinese: http://xn--fsqu00a.xn--0zwm56d
-        t = Patterns.WEB_URL.matcher("http://xn--fsqu00a.xn--0zwm56d").matches();
-        assertTrue("Valid URL", t);
-        t = Patterns.WEB_URL.matcher("xn--fsqu00a.xn--0zwm56d").matches();
-        assertTrue("Valid URL", t);
-
-        // Url for testing top level Arabic country code domain in Punycode:
-        //   http://xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx
-        t = Patterns.WEB_URL.matcher("http://xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx").matches();
-        assertTrue("Valid URL", t);
-        t = Patterns.WEB_URL.matcher("xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx").matches();
-        assertTrue("Valid URL", t);
-
-        // Internationalized URL.
-        t = Patterns.WEB_URL.matcher("http://\uD604\uAE08\uC601\uC218\uC99D.kr").matches();
-        assertTrue("Valid URL", t);
-        t = Patterns.WEB_URL.matcher("\uD604\uAE08\uC601\uC218\uC99D.kr").matches();
-        assertTrue("Valid URL", t);
-        // URL with international TLD.
-        t = Patterns.WEB_URL.matcher("\uB3C4\uBA54\uC778.\uD55C\uAD6D").matches();
-        assertTrue("Valid URL", t);
-
-        t = Patterns.WEB_URL.matcher("http://brainstormtech.blogs.fortune.cnn.com/2010/03/11/" +
-            "top-five-moments-from-eric-schmidt\u2019s-talk-in-abu-dhabi/").matches();
-        assertTrue("Valid URL", t);
-
-        t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches();
-        assertFalse("Matched invalid protocol", t);
-
-        t = Patterns.WEB_URL.matcher("http://www.example.com:8080").matches();
-        assertTrue("Didn't match valid URL with port", t);
-
-        t = Patterns.WEB_URL.matcher("http://www.example.com:8080/?foo=bar").matches();
-        assertTrue("Didn't match valid URL with port and query args", t);
-
-        t = Patterns.WEB_URL.matcher("http://www.example.com:8080/~user/?foo=bar").matches();
-        assertTrue("Didn't match valid URL with ~", t);
+    public void testIanaTopLevelDomains_matchesValidTld() throws Exception {
+        Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS);
+        assertTrue("Should match 'com'", pattern.matcher("com").matches());
     }
 
     @SmallTest
+    public void testIanaTopLevelDomains_matchesValidNewTld() throws Exception {
+        Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS);
+        assertTrue("Should match 'me'", pattern.matcher("me").matches());
+    }
+
+    @SmallTest
+    public void testIanaTopLevelDomains_matchesPunycodeTld() throws Exception {
+        Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS);
+        assertTrue("Should match Punycode TLD", pattern.matcher("xn--qxam").matches());
+    }
+
+    @SmallTest
+    public void testIanaTopLevelDomains_matchesIriTLD() throws Exception {
+        Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS);
+        assertTrue("Should match IRI TLD", pattern.matcher("\uD55C\uAD6D").matches());
+    }
+
+    @SmallTest
+    public void testIanaTopLevelDomains_doesNotMatchWrongTld() throws Exception {
+        Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS);
+        assertFalse("Should not match 'mem'", pattern.matcher("mem").matches());
+    }
+
+    @SmallTest
+    public void testIanaTopLevelDomains_doesNotMatchWrongPunycodeTld() throws Exception {
+        Pattern pattern = Pattern.compile(Patterns.IANA_TOP_LEVEL_DOMAINS);
+        assertFalse("Should not match invalid Punycode TLD", pattern.matcher("xn").matches());
+    }
+
+    //Tests for Patterns.WEB_URL
+
+    @SmallTest
+    public void testWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception {
+        String url = "http://www.android.com";
+        assertTrue("Should match URL with scheme and hostname",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesValidUrlWithSchemeHostnameAndNewTld() throws Exception {
+        String url = "http://www.android.me";
+        assertTrue("Should match URL with scheme, hostname and new TLD",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesValidUrlWithHostnameAndNewTld() throws Exception {
+        String url = "android.me";
+        assertTrue("Should match URL with hostname and new TLD",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesChinesePunycodeUrlWithProtocol() throws Exception {
+        String url = "http://xn--fsqu00a.xn--0zwm56d";
+        assertTrue("Should match Chinese Punycode URL with protocol",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesChinesePunycodeUrlWithoutProtocol() throws Exception {
+        String url = "xn--fsqu00a.xn--0zwm56d";
+        assertTrue("Should match Chinese Punycode URL without protocol",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+
+    @SmallTest
+    public void testWebUrl_matchesArabicPunycodeUrlWithProtocol() throws Exception {
+        String url = "http://xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx";
+        assertTrue("Should match arabic Punycode URL with protocol",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesArabicPunycodeUrlWithoutProtocol() throws Exception {
+        String url = "xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx";
+        assertTrue("Should match Arabic Punycode URL without protocol",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesUrlWithUnicodeDomainNameWithProtocol() throws Exception {
+        String url = "http://\uD604\uAE08\uC601\uC218\uC99D.kr";
+        assertTrue("Should match URL with Unicode domain name",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesUrlWithUnicodeDomainNameWithoutProtocol() throws Exception {
+        String url = "\uD604\uAE08\uC601\uC218\uC99D.kr";
+        assertTrue("Should match URL without protocol and with Unicode domain name",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesUrlWithUnicodeTld() throws Exception {
+        String url = "\uB3C4\uBA54\uC778.\uD55C\uAD6D";
+        assertTrue("Should match URL with Unicode TLD",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesUrlWithUnicodePath() throws Exception {
+        String url = "http://brainstormtech.blogs.fortune.cnn.com/2010/03/11/" +
+                "top-five-moments-from-eric-schmidt\u2019s-talk-in-abu-dhabi/";
+        assertTrue("Should match URL with Unicode path",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_doesNotMatchValidUrlWithInvalidProtocol() throws Exception {
+        String url = "ftp://www.example.com";
+        assertFalse("Should not match URL with invalid protocol",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesValidUrlWithPort() throws Exception {
+        String url = "http://www.example.com:8080";
+        assertTrue("Should match URL with port", Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesUrlWithPortAndQuery() throws Exception {
+        String url = "http://www.example.com:8080/?foo=bar";
+        assertTrue("Should match URL with port and query",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesUrlWithTilde() throws Exception {
+        String url = "http://www.example.com:8080/~user/?foo=bar";
+        assertTrue("Should match URL with tilde", Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testWebUrl_matchesProtocolCaseInsensitive() throws Exception {
+        String url = "hTtP://android.com";
+        assertTrue("Protocol matching should be case insensitive",
+                Patterns.WEB_URL.matcher(url).matches());
+    }
+
+    //Tests for Patterns.AUTOLINK_WEB_URL
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesValidUrlWithSchemeAndHostname() throws Exception {
+        String url = "http://www.android.com";
+        assertTrue("Should match URL with scheme and hostname",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesValidUrlWithSchemeHostnameAndNewTld() throws Exception {
+        String url = "http://www.android.me";
+        assertTrue("Should match URL with scheme, hostname and new TLD",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesValidUrlWithHostnameAndNewTld() throws Exception {
+        String url = "android.me";
+        assertTrue("Should match URL with hostname and new TLD",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+
+        url = "android.camera";
+        assertTrue("Should match URL with hostname and new TLD",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesChinesePunycodeUrlWithProtocol() throws Exception {
+        String url = "http://xn--fsqu00a.xn--0zwm56d";
+        assertTrue("Should match Chinese Punycode URL with protocol",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesChinesePunycodeUrlWithoutProtocol() throws Exception {
+        String url = "xn--fsqu00a.xn--0zwm56d";
+        assertTrue("Should match Chinese Punycode URL without protocol",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesArabicPunycodeUrlWithProtocol() throws Exception {
+        String url = "http://xn--4gbrim.xn--rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx";
+        assertTrue("Should match Arabic Punycode URL with protocol",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesArabicPunycodeUrlWithoutProtocol() throws Exception {
+        String url = "xn--4gbrim.xn--rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx";
+        assertTrue("Should match Arabic Punycode URL without protocol",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchPunycodeTldThatStartsWithDash() throws Exception {
+        String url = "http://xn--fsqu00a.-xn--0zwm56d";
+        assertFalse("Should not match Punycode TLD that starts with dash",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchPunycodeTldThatEndsWithDash() throws Exception {
+        String url = "http://xn--fsqu00a.xn--0zwm56d-";
+        assertFalse("Should not match Punycode TLD that ends with dash",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesUrlWithUnicodeDomainName() throws Exception {
+        String url = "http://\uD604\uAE08\uC601\uC218\uC99D.kr";
+        assertTrue("Should match URL with Unicode domain name",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+
+        url = "\uD604\uAE08\uC601\uC218\uC99D.kr";
+        assertTrue("hould match URL without protocol and with Unicode domain name",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesUrlWithUnicodeTld() throws Exception {
+        String url = "\uB3C4\uBA54\uC778.\uD55C\uAD6D";
+        assertTrue("Should match URL with Unicode TLD",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesUrlWithUnicodePath() throws Exception {
+        String url = "http://brainstormtech.blogs.fortune.cnn.com/2010/03/11/" +
+                "top-five-moments-from-eric-schmidt\u2019s-talk-in-abu-dhabi/";
+        assertTrue("Should match URL with Unicode path",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchValidUrlWithInvalidProtocol() throws Exception {
+        String url = "ftp://www.example.com";
+        assertFalse("Should not match URL with invalid protocol",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesValidUrlWithPort() throws Exception {
+        String url = "http://www.example.com:8080";
+        assertTrue("Should match URL with port",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesUrlWithPortAndQuery() throws Exception {
+        String url = "http://www.example.com:8080/?foo=bar";
+        assertTrue("Should match URL with port and query",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesUrlWithTilde() throws Exception {
+        String url = "http://www.example.com:8080/~user/?foo=bar";
+        assertTrue("Should match URL with tilde",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesProtocolCaseInsensitive() throws Exception {
+        String url = "hTtP://android.com";
+        assertTrue("Protocol matching should be case insensitive",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesUrlStartingWithHttpAndDoesNotHaveTld() throws Exception {
+        String url = "http://android/#notld///a/n/d/r/o/i/d&p1=1&p2=2";
+        assertTrue("Should match URL without a TLD and starting with http ",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchUrlsWithoutProtocolAndWithUnknownTld()
+            throws Exception {
+        String url = "thank.you";
+        assertFalse("Should not match URL that does not start with a protocol and " +
+                "does not contain a known TLD",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchUrlWithInvalidRequestParameter() throws Exception {
+        String url = "http://android.com?p=value";
+        assertFalse("Should not match URL with invalid request parameter",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotPartiallyMatchUnknownProtocol() throws Exception {
+        String url = "ftp://foo.bar/baz";
+        assertFalse("Should not partially match URL with unknown protocol",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).find());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesValidUrlWithEmoji() throws Exception {
+        String url = "Thank\u263A.com";
+        assertTrue("Should match URL with emoji",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchUrlsWithEmojiWithoutProtocolAndWithoutKnownTld()
+            throws Exception {
+        String url = "Thank\u263A.you";
+        assertFalse("Should not match URLs containing emoji and with unknown TLD",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchEmailAddress()
+            throws Exception {
+        String url = "android@android.com";
+        assertFalse("Should not match email address",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesDomainNameWithSurrogatePairs() throws Exception {
+        String url = "android\uD83C\uDF38.com";
+        assertTrue("Should match domain name with Unicode surrogate pairs",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesTldWithSurrogatePairs() throws Exception {
+        String url = "http://android.\uD83C\uDF38com";
+        assertTrue("Should match TLD with Unicode surrogate pairs",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_matchesPathWithSurrogatePairs() throws Exception {
+        String url = "http://android.com/path-with-\uD83C\uDF38?v=\uD83C\uDF38";
+        assertTrue("Should match path and query with Unicode surrogate pairs",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    @SmallTest
+    public void testAutoLinkWebUrl_doesNotMatchUrlWithExcludedSurrogate() throws Exception {
+        String url = "http://android\uD83F\uDFFE.com";
+        assertFalse("Should not match URL with excluded Unicode surrogate pair",
+                Patterns.AUTOLINK_WEB_URL.matcher(url).matches());
+    }
+
+    //Tests for Patterns.IP_ADDRESS
+
+    @SmallTest
     public void testIpPattern() throws Exception {
         boolean t;
 
@@ -118,34 +432,85 @@
         assertFalse("Invalid IP", t);
     }
 
+    //Tests for Patterns.DOMAIN_NAME
+
     @SmallTest
-    @Suppress // Failing.
-    public void testDomainPattern() throws Exception {
-        boolean t;
-
-        t = Patterns.DOMAIN_NAME.matcher("mail.example.com").matches();
-        assertTrue("Valid domain", t);
-
-        t = Patterns.DOMAIN_NAME.matcher("google.me").matches();
-        assertTrue("Valid domain", t);
-
-        // Internationalized domains.
-        t = Patterns.DOMAIN_NAME.matcher("\uD604\uAE08\uC601\uC218\uC99D.kr").matches();
-        assertTrue("Valid domain", t);
-
-        t = Patterns.DOMAIN_NAME.matcher("__+&42.xer").matches();
-        assertFalse("Invalid domain", t);
-
-        // Obsolete domain .yu
-        t = Patterns.DOMAIN_NAME.matcher("test.yu").matches();
-        assertFalse("Obsolete country code top level domain", t);
-
-        // Testing top level Arabic country code domain in Punycode:
-        t = Patterns.DOMAIN_NAME.matcher("xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c").matches();
-        assertTrue("Valid domain", t);
+    public void testDomain_matchesPunycodeTld() throws Exception {
+        String domain = "xn--fsqu00a.xn--0zwm56d";
+        assertTrue("Should match domain name in Punycode",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
     }
 
     @SmallTest
+    public void testDomain_doesNotMatchPunycodeThatStartsWithDash() throws Exception {
+        String domain = "xn--fsqu00a.-xn--0zwm56d";
+        assertFalse("Should not match Punycode TLD that starts with a dash",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_doesNotMatchPunycodeThatEndsWithDash() throws Exception {
+        String domain = "xn--fsqu00a.xn--0zwm56d-";
+        assertFalse("Should not match Punycode TLD that ends with a dash",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_doesNotMatchPunycodeLongerThanAllowed() throws Exception {
+        String tld = "xn--";
+        for(int i=0; i<=6; i++) {
+            tld += "0123456789";
+        }
+        String domain = "xn--fsqu00a." + tld;
+        assertFalse("Should not match Punycode TLD that is longer than 63 chars",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_matchesObsoleteTld() throws Exception {
+        String domain = "test.yu";
+        assertTrue("Should match domain names with obsolete TLD",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_matchesWithSubDomain() throws Exception {
+        String domain = "mail.example.com";
+        assertTrue("Should match domain names with subdomains",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_matchesWithoutSubDomain() throws Exception {
+        String domain = "android.me";
+        assertTrue("Should match domain names without subdomains",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_matchesUnicodeDomainNames() throws Exception {
+        String domain = "\uD604\uAE08\uC601\uC218\uC99D.kr";
+        assertTrue("Should match unicodedomain names",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_doesNotMatchInvalidDomain() throws Exception {
+        String domain = "__+&42.xer";
+        assertFalse("Should not match invalid domain name",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    @SmallTest
+    public void testDomain_matchesPunycodeArabicDomainName() throws Exception {
+        String domain = "xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c";
+        assertTrue("Should match Punycode Arabic domain name",
+                Patterns.DOMAIN_NAME.matcher(domain).matches());
+    }
+
+    //Tests for Patterns.PHONE
+
+    @SmallTest
     public void testPhonePattern() throws Exception {
         boolean t;
 
diff --git a/core/tests/coretests/src/android/widget/DatePickerActivity.java b/core/tests/coretests/src/android/widget/DatePickerActivity.java
new file mode 100644
index 0000000..c3b25a1
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/DatePickerActivity.java
@@ -0,0 +1,32 @@
+/*
+ * 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.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.frameworks.coretests.R;
+
+/**
+ * A minimal application for DatePickerFocusTest.
+ */
+public class DatePickerActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.datepicker_layout);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
new file mode 100644
index 0000000..513e40f
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.widget;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.android.frameworks.coretests.R;
+
+/**
+ * Test {@link DatePicker} focus changes.
+ */
+public class DatePickerFocusTest extends ActivityInstrumentationTestCase2<DatePickerActivity> {
+
+    private Activity mActivity;
+    private Instrumentation mInstrumentation;
+    private DatePicker mDatePicker;
+
+    public DatePickerFocusTest() {
+        super(DatePickerActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mActivity = getActivity();
+        mInstrumentation = getInstrumentation();
+
+        mDatePicker = (DatePicker) mActivity.findViewById(R.id.datePicker);
+    }
+
+    /**
+     * Tabs (forward and backward) through the DatePicker to ensure the correct
+     * Views gain focus.
+     */
+    public void testFocusTravel() throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(mDatePicker.requestFocus());
+            }
+        });
+        assertViewHasFocus(com.android.internal.R.id.date_picker_header_year);
+        sendKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.prev);
+        sendKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.next);
+        sendKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+        sendKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(R.id.belowPicker);
+        sendShiftKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+        sendShiftKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.next);
+        sendShiftKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.prev);
+        sendShiftKey(KeyEvent.KEYCODE_TAB);
+        assertViewHasFocus(com.android.internal.R.id.date_picker_header_year);
+    }
+
+    private void sendKey(int keycode) {
+        mInstrumentation.sendKeyDownUpSync(keycode);
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private void assertViewHasFocus(final int id) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                View view = mActivity.findViewById(id);
+                assertTrue(view.hasFocus());
+            }
+        });
+    }
+
+    private void sendShiftKey(int keycode) {
+        final KeyEvent shiftDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT);
+        mInstrumentation.sendKeySync(shiftDown);
+
+        final KeyEvent keyDown = new KeyEvent(SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0,
+                KeyEvent.META_SHIFT_ON);
+        mInstrumentation.sendKeySync(keyDown);
+
+        final KeyEvent keyUp = new KeyEvent(SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keycode, 0,
+                KeyEvent.META_SHIFT_ON);
+        mInstrumentation.sendKeySync(keyUp);
+
+        final KeyEvent shiftUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT);
+        mInstrumentation.sendKeySync(shiftUp);
+
+        mInstrumentation.waitForIdleSync();
+    }
+
+    /**
+     * Tests to ensure the keyboard can select the current year.
+     */
+    public void testYearChoice() throws Throwable {
+        setKnownDate();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                View year = mDatePicker.
+                        findViewById(com.android.internal.R.id.date_picker_header_year);
+                assertTrue(year.requestFocus());
+            }
+        });
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                View yearSelect = mDatePicker.
+                        findViewById(com.android.internal.R.id.date_picker_year_picker);
+                assertEquals(yearSelect, mDatePicker.findFocus());
+            }
+        });
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                View yearSelect = mDatePicker.
+                        findViewById(com.android.internal.R.id.date_picker_year_picker);
+                assertNotSame(View.VISIBLE, yearSelect.getVisibility());
+                View year = mDatePicker.
+                        findViewById(com.android.internal.R.id.date_picker_header_year);
+                assertTrue(year.hasFocus());
+                assertEquals(2014, mDatePicker.getYear());
+            }
+        });
+    }
+
+    public void testArrowThroughDays() throws Throwable {
+        setKnownDate();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                View prev = mDatePicker.findViewById(com.android.internal.R.id.next);
+                prev.requestFocus();
+            }
+        });
+        sendKey(KeyEvent.KEYCODE_TAB);
+        // Should select the current date and the date shouldn't change
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(12, 31, 2015);
+        // Move right to January 24, 2016
+        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(1, 24, 2016);
+        // Move down to January 31, 2016
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(1, 31, 2016);
+        // Move up to January 5, 2016
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(1, 5, 2016);
+        // Move up to prev arrow key
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        assertViewHasFocus(com.android.internal.R.id.prev);
+        // tab back into the day-selection pager
+        sendKey(KeyEvent.KEYCODE_TAB);
+        sendKey(KeyEvent.KEYCODE_TAB);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+        assertDateIs(1, 5, 2016);
+
+        // Move up out again, then down back into the day-selection pager.
+        // It should land right below the prev button (1/3/2016)
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+        assertDateIs(1, 3, 2016);
+
+        // Move left to previous month (12/12/2015)
+        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(12, 12, 2015);
+        // Now make sure the start of the month works
+        // Move up to 12/5/2015 and right to 1/1/2016
+        sendKey(KeyEvent.KEYCODE_DPAD_UP);
+        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(1, 1, 2016);
+        // Now make sure the left key goes back to previous month (12/5/2015)
+        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(12, 5, 2015);
+        // Now go to a mismatched row (no such row on previous month)
+        // This moves over to 1/31/2016 and then left to 12/31/2015
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
+        sendKey(KeyEvent.KEYCODE_ENTER);
+        assertDateIs(12, 31, 2015);
+    }
+
+    private void assertDateIs(int month, final int day, final int year) throws Throwable {
+        final int monthInt = month - 1; // months are 0-based
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(day, mDatePicker.getDayOfMonth());
+                assertEquals(year, mDatePicker.getYear());
+                assertEquals(monthInt, mDatePicker.getMonth());
+            }
+        });
+    }
+
+    private void setKnownDate() throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mDatePicker.updateDate(2015, 11, 31); // December 31, 2015
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 48590c1..1966313 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -22,6 +22,7 @@
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
 import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -32,6 +33,8 @@
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 
+import java.util.List;
+
 /**
  * Tests {@link PhoneWindow}'s {@link ActionMode} related methods.
  */
@@ -288,6 +291,9 @@
 
         @Override
         public void onActionModeFinished(ActionMode mode) {}
+
+        @Override
+        public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {}
     }
 
     private static final class MockActionModeCallback implements ActionMode.Callback {
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 554b6f1..731bf42 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -344,7 +344,7 @@
     <family>
         <font weight="400" style="normal">NanumGothic.ttf</font>
     </family>
-    <family lang="und-Qaae">
+    <family lang="und-Zsye">
         <font weight="400" style="normal">NotoColorEmoji.ttf</font>
     </family>
     <family>
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index c05ea2e..704f0ce 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -38,13 +38,14 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * The main programming interface for the DRM framework. An application must instantiate this class
  * to access DRM agents through the DRM framework.
  *
  */
-public class DrmManagerClient {
+public class DrmManagerClient implements AutoCloseable {
     /**
      * Indicates that a request was successful or that no error occurred.
      */
@@ -61,6 +62,7 @@
     HandlerThread mEventThread;
     private static final String TAG = "DrmManagerClient";
 
+    private final AtomicBoolean mClosed = new AtomicBoolean();
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     static {
@@ -117,7 +119,6 @@
 
     private int mUniqueId;
     private long mNativeContext;
-    private volatile boolean mReleased;
     private Context mContext;
     private InfoHandler mInfoHandler;
     private EventHandler mEventHandler;
@@ -261,41 +262,47 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            if (mCloseGuard != null) {
-                mCloseGuard.warnIfOpen();
-            }
-            release();
+            mCloseGuard.warnIfOpen();
+            close();
         } finally {
             super.finalize();
         }
     }
 
     /**
-     * Releases resources associated with the current session of DrmManagerClient.
-     *
-     * It is considered good practice to call this method when the {@link DrmManagerClient} object
-     * is no longer needed in your application. After release() is called,
-     * {@link DrmManagerClient} is no longer usable since it has lost all of its required resource.
+     * Releases resources associated with the current session of
+     * DrmManagerClient. It is considered good practice to call this method when
+     * the {@link DrmManagerClient} object is no longer needed in your
+     * application. After this method is called, {@link DrmManagerClient} is no
+     * longer usable since it has lost all of its required resource.
      */
-    public void release() {
-        if (mReleased) return;
-        mReleased = true;
-
-        if (mEventHandler != null) {
-            mEventThread.quit();
-            mEventThread = null;
-        }
-        if (mInfoHandler != null) {
-            mInfoThread.quit();
-            mInfoThread = null;
-        }
-        mEventHandler = null;
-        mInfoHandler = null;
-        mOnEventListener = null;
-        mOnInfoListener = null;
-        mOnErrorListener = null;
-        _release(mUniqueId);
+    @Override
+    public void close() {
         mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
+            if (mEventHandler != null) {
+                mEventThread.quit();
+                mEventThread = null;
+            }
+            if (mInfoHandler != null) {
+                mInfoThread.quit();
+                mInfoThread = null;
+            }
+            mEventHandler = null;
+            mInfoHandler = null;
+            mOnEventListener = null;
+            mOnInfoListener = null;
+            mOnErrorListener = null;
+            _release(mUniqueId);
+        }
+    }
+
+    /**
+     * @deprecated replaced by {@link #close()}.
+     */
+    @Deprecated
+    public void release() {
+        close();
     }
 
     /**
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b5b1a25..169ef0b 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -25,14 +25,14 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 
-import dalvik.system.VMRuntime;
-
 import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
 
+import libcore.util.NativeAllocationRegistry;
+
 public final class Bitmap implements Parcelable {
     private static final String TAG = "Bitmap";
 
@@ -44,6 +44,10 @@
      */
     public static final int DENSITY_NONE = 0;
 
+    // Estimated size of the Bitmap native allocation, not including
+    // pixel data.
+    private static final long NATIVE_ALLOCATION_SIZE = 32;
+
     /**
      * Backing buffer for the Bitmap.
      */
@@ -51,7 +55,6 @@
 
     // Convenience for JNI access
     private final long mNativePtr;
-    private final BitmapFinalizer mFinalizer;
 
     private final boolean mIsMutable;
 
@@ -125,9 +128,13 @@
         }
 
         mNativePtr = nativeBitmap;
-        mFinalizer = new BitmapFinalizer(nativeBitmap);
-        int nativeAllocationByteCount = (buffer == null ? getByteCount() : 0);
-        mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount);
+        long nativeSize = NATIVE_ALLOCATION_SIZE;
+        if (buffer == null) {
+            nativeSize += getByteCount();
+        }
+        NativeAllocationRegistry registry = new NativeAllocationRegistry(
+            nativeGetNativeFinalizer(), nativeSize);
+        registry.registerNativeAllocation(this, nativeBitmap);
     }
 
     /**
@@ -253,7 +260,7 @@
             throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
         }
 
-        nativeReconfigure(mFinalizer.mNativeBitmap, width, height, config.nativeInt,
+        nativeReconfigure(mNativePtr, width, height, config.nativeInt,
                 mBuffer.length, mRequestPremultiplied);
         mWidth = width;
         mHeight = height;
@@ -330,8 +337,8 @@
      * there are no more references to this bitmap.
      */
     public void recycle() {
-        if (!mRecycled && mFinalizer.mNativeBitmap != 0) {
-            if (nativeRecycle(mFinalizer.mNativeBitmap)) {
+        if (!mRecycled && mNativePtr != 0) {
+            if (nativeRecycle(mNativePtr)) {
                 // return value indicates whether native pixel object was actually recycled.
                 // false indicates that it is still in use at the native level and these
                 // objects should not be collected now. They will be collected later when the
@@ -364,7 +371,7 @@
         if (mRecycled) {
             Log.w(TAG, "Called getGenerationId() on a recycle()'d bitmap! This is undefined behavior!");
         }
-        return nativeGenerationId(mFinalizer.mNativeBitmap);
+        return nativeGenerationId(mNativePtr);
     }
 
     /**
@@ -520,7 +527,7 @@
             throw new RuntimeException("Buffer not large enough for pixels");
         }
 
-        nativeCopyPixelsToBuffer(mFinalizer.mNativeBitmap, dst);
+        nativeCopyPixelsToBuffer(mNativePtr, dst);
 
         // now update the buffer's position
         int position = dst.position();
@@ -560,7 +567,7 @@
             throw new RuntimeException("Buffer not large enough for pixels");
         }
 
-        nativeCopyPixelsFromBuffer(mFinalizer.mNativeBitmap, src);
+        nativeCopyPixelsFromBuffer(mNativePtr, src);
 
         // now update the buffer's position
         int position = src.position();
@@ -582,7 +589,7 @@
      */
     public Bitmap copy(Config config, boolean isMutable) {
         checkRecycled("Can't copy a recycled bitmap");
-        Bitmap b = nativeCopy(mFinalizer.mNativeBitmap, config.nativeInt, isMutable);
+        Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
         if (b != null) {
             b.setPremultiplied(mRequestPremultiplied);
             b.mDensity = mDensity;
@@ -598,7 +605,7 @@
      */
     public Bitmap createAshmemBitmap() {
         checkRecycled("Can't copy a recycled bitmap");
-        Bitmap b = nativeCopyAshmem(mFinalizer.mNativeBitmap);
+        Bitmap b = nativeCopyAshmem(mNativePtr);
         if (b != null) {
             b.setPremultiplied(mRequestPremultiplied);
             b.mDensity = mDensity;
@@ -859,7 +866,7 @@
         }
         bm.setHasAlpha(hasAlpha);
         if (config == Config.ARGB_8888 && !hasAlpha) {
-            nativeErase(bm.mFinalizer.mNativeBitmap, 0xff000000);
+            nativeErase(bm.mNativePtr, 0xff000000);
         }
         // No need to initialize the bitmap to zeroes with other configs;
         // it is backed by a VM byte array which is by definition preinitialized
@@ -1049,7 +1056,7 @@
             throw new IllegalArgumentException("quality must be 0..100");
         }
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
-        boolean result = nativeCompress(mFinalizer.mNativeBitmap, format.nativeInt,
+        boolean result = nativeCompress(mNativePtr, format.nativeInt,
                 quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         return result;
@@ -1093,7 +1100,7 @@
         if (mRecycled) {
             Log.w(TAG, "Called isPremultiplied() on a recycle()'d bitmap! This is undefined behavior!");
         }
-        return nativeIsPremultiplied(mFinalizer.mNativeBitmap);
+        return nativeIsPremultiplied(mNativePtr);
     }
 
     /**
@@ -1119,7 +1126,7 @@
     public final void setPremultiplied(boolean premultiplied) {
         checkRecycled("setPremultiplied called on a recycled bitmap");
         mRequestPremultiplied = premultiplied;
-        nativeSetPremultiplied(mFinalizer.mNativeBitmap, premultiplied);
+        nativeSetPremultiplied(mNativePtr, premultiplied);
     }
 
     /** Returns the bitmap's width */
@@ -1220,7 +1227,7 @@
         if (mRecycled) {
             Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
         }
-        return nativeRowBytes(mFinalizer.mNativeBitmap);
+        return nativeRowBytes(mNativePtr);
     }
 
     /**
@@ -1266,7 +1273,7 @@
         if (mRecycled) {
             Log.w(TAG, "Called getConfig() on a recycle()'d bitmap! This is undefined behavior!");
         }
-        return Config.nativeToConfig(nativeConfig(mFinalizer.mNativeBitmap));
+        return Config.nativeToConfig(nativeConfig(mNativePtr));
     }
 
     /** Returns true if the bitmap's config supports per-pixel alpha, and
@@ -1281,7 +1288,7 @@
         if (mRecycled) {
             Log.w(TAG, "Called hasAlpha() on a recycle()'d bitmap! This is undefined behavior!");
         }
-        return nativeHasAlpha(mFinalizer.mNativeBitmap);
+        return nativeHasAlpha(mNativePtr);
     }
 
     /**
@@ -1296,7 +1303,7 @@
      */
     public void setHasAlpha(boolean hasAlpha) {
         checkRecycled("setHasAlpha called on a recycled bitmap");
-        nativeSetHasAlpha(mFinalizer.mNativeBitmap, hasAlpha, mRequestPremultiplied);
+        nativeSetHasAlpha(mNativePtr, hasAlpha, mRequestPremultiplied);
     }
 
     /**
@@ -1320,7 +1327,7 @@
         if (mRecycled) {
             Log.w(TAG, "Called hasMipMap() on a recycle()'d bitmap! This is undefined behavior!");
         }
-        return nativeHasMipMap(mFinalizer.mNativeBitmap);
+        return nativeHasMipMap(mNativePtr);
     }
 
     /**
@@ -1345,7 +1352,7 @@
      */
     public final void setHasMipMap(boolean hasMipMap) {
         checkRecycled("setHasMipMap called on a recycled bitmap");
-        nativeSetHasMipMap(mFinalizer.mNativeBitmap, hasMipMap);
+        nativeSetHasMipMap(mNativePtr, hasMipMap);
     }
 
     /**
@@ -1358,7 +1365,7 @@
         if (!isMutable()) {
             throw new IllegalStateException("cannot erase immutable bitmaps");
         }
-        nativeErase(mFinalizer.mNativeBitmap, c);
+        nativeErase(mNativePtr, c);
     }
 
     /**
@@ -1375,7 +1382,7 @@
     public int getPixel(int x, int y) {
         checkRecycled("Can't call getPixel() on a recycled bitmap");
         checkPixelAccess(x, y);
-        return nativeGetPixel(mFinalizer.mNativeBitmap, x, y);
+        return nativeGetPixel(mNativePtr, x, y);
     }
 
     /**
@@ -1408,7 +1415,7 @@
             return; // nothing to do
         }
         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
-        nativeGetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
+        nativeGetPixels(mNativePtr, pixels, offset, stride,
                         x, y, width, height);
     }
 
@@ -1489,7 +1496,7 @@
             throw new IllegalStateException();
         }
         checkPixelAccess(x, y);
-        nativeSetPixel(mFinalizer.mNativeBitmap, x, y, color);
+        nativeSetPixel(mNativePtr, x, y, color);
     }
 
     /**
@@ -1525,7 +1532,7 @@
             return; // nothing to do
         }
         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
-        nativeSetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
+        nativeSetPixels(mNativePtr, pixels, offset, stride,
                         x, y, width, height);
     }
 
@@ -1563,7 +1570,7 @@
      */
     public void writeToParcel(Parcel p, int flags) {
         checkRecycled("Can't parcel a recycled bitmap");
-        if (!nativeWriteToParcel(mFinalizer.mNativeBitmap, mIsMutable, mDensity, p)) {
+        if (!nativeWriteToParcel(mNativePtr, mIsMutable, mDensity, p)) {
             throw new RuntimeException("native writeToParcel failed");
         }
     }
@@ -1609,7 +1616,7 @@
     public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
         checkRecycled("Can't extractAlpha on a recycled bitmap");
         long nativePaint = paint != null ? paint.getNativeInstance() : 0;
-        Bitmap bm = nativeExtractAlpha(mFinalizer.mNativeBitmap, nativePaint, offsetXY);
+        Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY);
         if (bm == null) {
             throw new RuntimeException("Failed to extractAlpha on Bitmap");
         }
@@ -1629,7 +1636,7 @@
         if (other.isRecycled()) {
             throw new IllegalArgumentException("Can't compare to a recycled bitmap!");
         }
-        return nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap);
+        return nativeSameAs(mNativePtr, other.mNativePtr);
     }
 
     /**
@@ -1660,41 +1667,6 @@
         return nativeRefPixelRef(mNativePtr);
     }
 
-    private static class BitmapFinalizer {
-        private long mNativeBitmap;
-
-        // Native memory allocated for the duration of the Bitmap,
-        // if pixel data allocated into native memory, instead of java byte[]
-        private int mNativeAllocationByteCount;
-
-        BitmapFinalizer(long nativeBitmap) {
-            mNativeBitmap = nativeBitmap;
-        }
-
-        public void setNativeAllocationByteCount(int nativeByteCount) {
-            if (mNativeAllocationByteCount != 0) {
-                VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount);
-            }
-            mNativeAllocationByteCount = nativeByteCount;
-            if (mNativeAllocationByteCount != 0) {
-                VMRuntime.getRuntime().registerNativeAllocation(mNativeAllocationByteCount);
-            }
-        }
-
-        @Override
-        public void finalize() {
-            try {
-                super.finalize();
-            } catch (Throwable t) {
-                // Ignore
-            } finally {
-                setNativeAllocationByteCount(0);
-                nativeDestructor(mNativeBitmap);
-                mNativeBitmap = 0;
-            }
-        }
-    }
-
     //////////// native methods
 
     private static native Bitmap nativeCreate(int[] colors, int offset,
@@ -1703,7 +1675,7 @@
     private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
                                             boolean isMutable);
     private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
-    private static native void nativeDestructor(long nativeBitmap);
+    private static native long nativeGetNativeFinalizer();
     private static native boolean nativeRecycle(long nativeBitmap);
     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
                                                  int config, int allocSize,
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index da58884..447a4c4 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -163,8 +163,11 @@
         public boolean inPremultiplied;
 
         /**
-         * If dither is true, the decoder will attempt to dither the decoded
-         * image.
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this is
+         * ignored.
+         *
+         * In {@link android.os.Build.VERSION_CODES#M} and below, if dither is
+         * true, the decoder will attempt to dither the decoded image.
          */
         public boolean inDither;
 
@@ -308,7 +311,11 @@
         public boolean inInputShareable;
 
         /**
-         * If inPreferQualityOverSpeed is set to true, the decoder will try to
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this is
+         * ignored.  The output will always be high quality.
+         *
+         * In {@link android.os.Build.VERSION_CODES#M} and below, if
+         * inPreferQualityOverSpeed is set to true, the decoder will try to
          * decode the reconstructed image to a higher quality even at the
          * expense of the decoding speed. Currently the field only affects JPEG
          * decode, in the case of which a more accurate, but slightly slower,
@@ -347,9 +354,10 @@
          */
         public byte[] inTempStorage;
 
-        private native void requestCancel();
-
         /**
+         * @deprecated As of {@link android.os.Build.VERSION_CODES#N}, see
+         * comments on {@link #requestCancelDecode()}.
+         *
          * Flag to indicate that cancel has been called on this object.  This
          * is useful if there's an intermediary that wants to first decode the
          * bounds and then decode the image.  In that case the intermediary
@@ -359,16 +367,19 @@
         public boolean mCancel;
 
         /**
-         *  This can be called from another thread while this options object is
-         *  inside a decode... call. Calling this will notify the decoder that
-         *  it should cancel its operation. This is not guaranteed to cancel
-         *  the decode, but if it does, the decoder... operation will return
-         *  null, or if inJustDecodeBounds is true, will set outWidth/outHeight
+         *  @deprecated As of {@link android.os.Build.VERSION_CODES#N}, this
+         *  will not affect the decode, though it will still set mCancel.
+         *
+         *  In {@link android.os.Build.VERSION_CODES#M} and below, if this can
+         *  be called from another thread while this options object is inside
+         *  a decode... call. Calling this will notify the decoder that it
+         *  should cancel its operation. This is not guaranteed to cancel the
+         *  decode, but if it does, the decoder... operation will return null,
+         *  or if inJustDecodeBounds is true, will set outWidth/outHeight
          *  to -1
          */
         public void requestCancelDecode() {
             mCancel = true;
-            requestCancel();
         }
     }
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index d5166ab..d4f745d 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -31,6 +31,8 @@
 
 import javax.microedition.khronos.opengles.GL;
 
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * The Canvas class holds the "draw" calls. To draw something, you need
  * 4 basic components: A Bitmap to hold the pixels, a Canvas to host
@@ -50,7 +52,7 @@
 
     /**
      * Should only be assigned in constructors (or setBitmap if software canvas),
-     * freed in finalizer.
+     * freed by NativeAllocation.
      * @hide
      */
     protected long mNativeCanvasWrapper;
@@ -85,32 +87,15 @@
     // (see SkCanvas.cpp, SkDraw.cpp)
     private static final int MAXMIMUM_BITMAP_SIZE = 32766;
 
+    // The approximate size of the native allocation associated with
+    // a Canvas object.
+    private static final long NATIVE_ALLOCATION_SIZE = 525;
+
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+        getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+
     // This field is used to finalize the native Canvas properly
-    private final CanvasFinalizer mFinalizer;
-
-    private static final class CanvasFinalizer {
-        private long mNativeCanvasWrapper;
-
-        public CanvasFinalizer(long nativeCanvas) {
-            mNativeCanvasWrapper = nativeCanvas;
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            try {
-                dispose();
-            } finally {
-                super.finalize();
-            }
-        }
-
-        public void dispose() {
-            if (mNativeCanvasWrapper != 0) {
-                finalizer(mNativeCanvasWrapper);
-                mNativeCanvasWrapper = 0;
-            }
-        }
-    }
+    private Runnable mFinalizer;
 
     /**
      * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to
@@ -122,7 +107,7 @@
         if (!isHardwareAccelerated()) {
             // 0 means no native bitmap
             mNativeCanvasWrapper = initRaster(null);
-            mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
+            mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper);
         } else {
             mFinalizer = null;
         }
@@ -143,7 +128,7 @@
         }
         throwIfCannotDraw(bitmap);
         mNativeCanvasWrapper = initRaster(bitmap);
-        mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
+        mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper);
         mBitmap = bitmap;
         mDensity = bitmap.mDensity;
     }
@@ -154,7 +139,7 @@
             throw new IllegalStateException();
         }
         mNativeCanvasWrapper = nativeCanvas;
-        mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
+        mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper);
         mDensity = Bitmap.getDefaultDensity();
     }
 
@@ -654,6 +639,12 @@
     /**
      * Return, in ctm, the current transformation matrix. This does not alter
      * the matrix in the canvas, but just returns a copy of it.
+     *
+     * @deprecated {@link #isHardwareAccelerated() Hardware accelerated} canvases may have any
+     * matrix when passed to a View or Drawable, as it is implementation defined where in the
+     * hierarchy such canvases are created. It is recommended in such cases to either draw contents
+     * irrespective of the current matrix, or to track relevant transform state outside of the
+     * canvas.
      */
     @Deprecated
     public void getMatrix(@NonNull Matrix ctm) {
@@ -663,6 +654,12 @@
     /**
      * Return a new matrix with a copy of the canvas' current transformation
      * matrix.
+     *
+     * @deprecated {@link #isHardwareAccelerated() Hardware accelerated} canvases may have any
+     * matrix when passed to a View or Drawable, as it is implementation defined where in the
+     * hierarchy such canvases are created. It is recommended in such cases to either draw contents
+     * irrespective of the current matrix, or to track relevant transform state outside of the
+     * canvas.
      */
     @Deprecated
     public final @NonNull Matrix getMatrix() {
@@ -812,6 +809,7 @@
      * @deprecated Unlike all other clip calls this API does not respect the
      *             current matrix. Use {@link #clipRect(Rect)} as an alternative.
      */
+    @Deprecated
     public boolean clipRegion(@NonNull Region region, @NonNull Region.Op op) {
         return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt);
     }
@@ -829,6 +827,7 @@
      * @deprecated Unlike all other clip calls this API does not respect the
      *             current matrix. Use {@link #clipRect(Rect)} as an alternative.
      */
+    @Deprecated
     public boolean clipRegion(@NonNull Region region) {
         return clipRegion(region, Region.Op.INTERSECT);
     }
@@ -1830,16 +1829,16 @@
      * Draw the text in the array, with each character's origin specified by
      * the pos array.
      *
-     * This method does not support glyph composition and decomposition and
-     * should therefore not be used to render complex scripts. It also doesn't
-     * handle supplementary characters (eg emoji).
-     *
      * @param text     The text to be drawn
      * @param index    The index of the first character to draw
      * @param count    The number of characters to draw, starting from index.
      * @param pos      Array of [x,y] positions, used to position each
      *                 character
      * @param paint    The paint used for the text (e.g. color, size, style)
+     *
+     * @deprecated This method does not support glyph composition and decomposition and
+     * should therefore not be used to render complex scripts. It also doesn't
+     * handle supplementary characters (eg emoji).
      */
     @Deprecated
     public void drawPosText(@NonNull char[] text, int index, int count,
@@ -1857,13 +1856,13 @@
      * Draw the text in the array, with each character's origin specified by
      * the pos array.
      *
-     * This method does not support glyph composition and decomposition and
-     * should therefore not be used to render complex scripts. It also doesn't
-     * handle supplementary characters (eg emoji).
-     *
      * @param text  The text to be drawn
      * @param pos   Array of [x,y] positions, used to position each character
      * @param paint The paint used for the text (e.g. color, size, style)
+     *
+     * @deprecated This method does not support glyph composition and decomposition and
+     * should therefore not be used to render complex scripts. It also doesn't
+     * handle supplementary characters (eg emoji).
      */
     @Deprecated
     public void drawPosText(@NonNull String text, @NonNull @Size(multiple=2) float[] pos,
@@ -1966,7 +1965,11 @@
      * @hide
      */
     public void release() {
-        mFinalizer.dispose();
+        mNativeCanvasWrapper = 0;
+        if (mFinalizer != null) {
+            mFinalizer.run();
+            mFinalizer = null;
+        }
     }
 
     /**
@@ -2134,5 +2137,5 @@
                                                      float hOffset,
                                                      float vOffset,
                                                      int flags, long nativePaint, long nativeTypeface);
-    private static native void finalizer(long nativeCanvas);
+    private static native long getNativeFinalizer();
 }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 90522f7..dfb8bb8 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -30,6 +30,8 @@
 import java.util.HashMap;
 import java.util.Locale;
 
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * The Paint class holds the style and color information about how to draw
  * geometries, text and bitmaps.
@@ -39,6 +41,12 @@
     private long mNativePaint;
     private long mNativeShader = 0;
 
+    // The approximate size of a native paint object.
+    private static final long NATIVE_PAINT_SIZE = 98;
+
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+        nGetNativeFinalizer(), NATIVE_PAINT_SIZE);
+
     /**
      * @hide
      */
@@ -444,6 +452,7 @@
      */
     public Paint(int flags) {
         mNativePaint = nInit();
+        sRegistry.registerNativeAllocation(this, mNativePaint);
         setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
         // TODO: Turning off hinting has undesirable side effects, we need to
         //       revisit hinting once we add support for subpixel positioning
@@ -462,12 +471,12 @@
      */
     public Paint(Paint paint) {
         mNativePaint = nInitWithPaint(paint.getNativeInstance());
+        sRegistry.registerNativeAllocation(this, mNativePaint);
         setClassVariablesFrom(paint);
     }
 
     /** Restores the paint to its default settings. */
     public void reset() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nReset(mNativePaint);
         setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
 
@@ -502,8 +511,6 @@
      * methods on this.
      */
     public void set(Paint src) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
-        if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
         if (this != src) {
             // copy over the native settings
             nSet(mNativePaint, src.mNativePaint);
@@ -554,7 +561,6 @@
      * @hide
      */
     public long getNativeInstance() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
         if (newNativeShader != mNativeShader) {
             mNativeShader = newNativeShader;
@@ -592,7 +598,6 @@
      * @return the paint's flags (see enums ending in _Flag for bit masks)
      */
     public int getFlags() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetFlags(mNativePaint);
     }
 
@@ -604,7 +609,6 @@
      * @param flags The new flag bits for the paint
      */
     public void setFlags(int flags) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetFlags(mNativePaint, flags);
     }
 
@@ -615,7 +619,6 @@
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
      */
     public int getHinting() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetHinting(mNativePaint);
     }
 
@@ -626,7 +629,6 @@
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
      */
     public void setHinting(int mode) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetHinting(mNativePaint, mode);
     }
 
@@ -653,7 +655,6 @@
      * @param aa true to set the antialias bit in the flags, false to clear it
      */
     public void setAntiAlias(boolean aa) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetAntiAlias(mNativePaint, aa);
     }
 
@@ -684,7 +685,6 @@
      * @param dither true to set the dithering bit in flags, false to clear it
      */
     public void setDither(boolean dither) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetDither(mNativePaint, dither);
     }
 
@@ -706,7 +706,6 @@
      *                   false to clear it.
      */
     public void setLinearText(boolean linearText) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetLinearText(mNativePaint, linearText);
     }
 
@@ -728,7 +727,6 @@
      *                     flags, false to clear it.
      */
     public void setSubpixelText(boolean subpixelText) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetSubpixelText(mNativePaint, subpixelText);
     }
 
@@ -750,7 +748,6 @@
      *                      flags, false to clear it.
      */
     public void setUnderlineText(boolean underlineText) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetUnderlineText(mNativePaint, underlineText);
     }
 
@@ -772,7 +769,6 @@
      *                       flags, false to clear it.
      */
     public void setStrikeThruText(boolean strikeThruText) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetStrikeThruText(mNativePaint, strikeThruText);
     }
 
@@ -794,7 +790,6 @@
      *                     flags, false to clear it.
      */
     public void setFakeBoldText(boolean fakeBoldText) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetFakeBoldText(mNativePaint, fakeBoldText);
     }
 
@@ -822,7 +817,6 @@
      *               flags, false to clear it.
      */
     public void setFilterBitmap(boolean filter) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetFilterBitmap(mNativePaint, filter);
     }
 
@@ -836,7 +830,6 @@
      * @return the paint's style setting (Fill, Stroke, StrokeAndFill)
      */
     public Style getStyle() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return sStyleArray[nGetStyle(mNativePaint)];
     }
 
@@ -848,7 +841,6 @@
      * @param style The new style to set in the paint
      */
     public void setStyle(Style style) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetStyle(mNativePaint, style.nativeInt);
     }
 
@@ -862,7 +854,6 @@
      */
     @ColorInt
     public int getColor() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetColor(mNativePaint);
     }
 
@@ -877,7 +868,6 @@
      * @param color The new color (including alpha) to set in the paint.
      */
     public void setColor(@ColorInt int color) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetColor(mNativePaint, color);
     }
 
@@ -891,7 +881,6 @@
      * @return the alpha component of the paint's color.
      */
     public int getAlpha() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetAlpha(mNativePaint);
     }
 
@@ -905,7 +894,6 @@
      * @param a set the alpha component [0..255] of the paint's color.
      */
     public void setAlpha(int a) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetAlpha(mNativePaint, a);
     }
 
@@ -933,7 +921,6 @@
      *         Stroke or StrokeAndFill.
      */
     public float getStrokeWidth() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetStrokeWidth(mNativePaint);
     }
 
@@ -948,7 +935,6 @@
      *              style is Stroke or StrokeAndFill.
      */
     public void setStrokeWidth(float width) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetStrokeWidth(mNativePaint, width);
     }
 
@@ -962,7 +948,6 @@
      *         Stroke or StrokeAndFill.
      */
     public float getStrokeMiter() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetStrokeMiter(mNativePaint);
     }
 
@@ -976,7 +961,6 @@
      *              style is Stroke or StrokeAndFill.
      */
     public void setStrokeMiter(float miter) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetStrokeMiter(mNativePaint, miter);
     }
 
@@ -990,7 +974,6 @@
      *         style is Stroke or StrokeAndFill.
      */
     public Cap getStrokeCap() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return sCapArray[nGetStrokeCap(mNativePaint)];
     }
 
@@ -1001,7 +984,6 @@
      *            style is Stroke or StrokeAndFill.
      */
     public void setStrokeCap(Cap cap) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetStrokeCap(mNativePaint, cap.nativeInt);
     }
 
@@ -1011,7 +993,6 @@
      * @return the paint's Join.
      */
     public Join getStrokeJoin() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return sJoinArray[nGetStrokeJoin(mNativePaint)];
     }
 
@@ -1022,7 +1003,6 @@
      *             Stroke or StrokeAndFill.
      */
     public void setStrokeJoin(Join join) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetStrokeJoin(mNativePaint, join.nativeInt);
     }
 
@@ -1038,7 +1018,6 @@
      *                 drawn with a hairline (width == 0)
      */
     public boolean getFillPath(Path src, Path dst) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetFillPath(mNativePaint, src.ni(), dst.ni());
     }
 
@@ -1061,6 +1040,11 @@
      * @return       shader
      */
     public Shader setShader(Shader shader) {
+        // If mShader changes, cached value of native shader aren't valid, since
+        // old shader's pointer may be reused by another shader allocation later
+        if (mShader != shader) {
+            mNativeShader = -1;
+        }
         // Defer setting the shader natively until getNativeInstance() is called
         mShader = shader;
         return shader;
@@ -1082,7 +1066,6 @@
      * @return       filter
      */
     public ColorFilter setColorFilter(ColorFilter filter) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long filterNative = 0;
         if (filter != null)
             filterNative = filter.native_instance;
@@ -1110,7 +1093,6 @@
      * @return         xfermode
      */
     public Xfermode setXfermode(Xfermode xfermode) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long xfermodeNative = 0;
         if (xfermode != null)
             xfermodeNative = xfermode.native_instance;
@@ -1138,7 +1120,6 @@
      * @return       effect
      */
     public PathEffect setPathEffect(PathEffect effect) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long effectNative = 0;
         if (effect != null) {
             effectNative = effect.native_instance;
@@ -1168,7 +1149,6 @@
      * @return           maskfilter
      */
     public MaskFilter setMaskFilter(MaskFilter maskfilter) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long maskfilterNative = 0;
         if (maskfilter != null) {
             maskfilterNative = maskfilter.native_instance;
@@ -1200,7 +1180,6 @@
      * @return         typeface
      */
     public Typeface setTypeface(Typeface typeface) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long typefaceNative = 0;
         if (typeface != null) {
             typefaceNative = typeface.native_instance;
@@ -1239,7 +1218,6 @@
      */
     @Deprecated
     public Rasterizer setRasterizer(Rasterizer rasterizer) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long rasterizerNative = 0;
         if (rasterizer != null) {
             rasterizerNative = rasterizer.native_instance;
@@ -1262,7 +1240,6 @@
      * opaque, or the alpha from the shadow color if not.
      */
     public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
       nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
     }
 
@@ -1280,7 +1257,6 @@
      * @hide
      */
     public boolean hasShadowLayer() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nHasShadowLayer(mNativePaint);
     }
 
@@ -1293,7 +1269,6 @@
      * @return the paint's Align value for drawing text.
      */
     public Align getTextAlign() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return sAlignArray[nGetTextAlign(mNativePaint)];
     }
 
@@ -1306,7 +1281,6 @@
      * @param align set the paint's Align value for drawing text.
      */
     public void setTextAlign(Align align) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetTextAlign(mNativePaint, align.nativeInt);
     }
 
@@ -1340,7 +1314,6 @@
      * @param locale the paint's locale value for drawing text, must not be null.
      */
     public void setTextLocale(@NonNull Locale locale) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (locale == null) {
             throw new IllegalArgumentException("locale cannot be null");
         }
@@ -1379,7 +1352,6 @@
      * @param locales the paint's locale list for drawing text, must not be null or empty.
      */
     public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (locales == null || locales.isEmpty()) {
             throw new IllegalArgumentException("locales cannot be null or empty");
         }
@@ -1408,7 +1380,6 @@
      * @return true if elegant metrics are enabled for text drawing.
      */
     public boolean isElegantTextHeight() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nIsElegantTextHeight(mNativePaint);
     }
 
@@ -1422,7 +1393,6 @@
      * @param elegant set the paint's elegant metrics flag for drawing text.
      */
     public void setElegantTextHeight(boolean elegant) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetElegantTextHeight(mNativePaint, elegant);
     }
 
@@ -1434,7 +1404,6 @@
      * @return the paint's text size.
      */
     public float getTextSize() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetTextSize(mNativePaint);
     }
 
@@ -1446,7 +1415,6 @@
      * @param textSize set the paint's text size.
      */
     public void setTextSize(float textSize) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetTextSize(mNativePaint, textSize);
     }
 
@@ -1459,7 +1427,6 @@
      * @return the paint's scale factor in X for drawing/measuring text
      */
     public float getTextScaleX() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetTextScaleX(mNativePaint);
     }
 
@@ -1473,7 +1440,6 @@
      * @param scaleX set the paint's scale in X for drawing/measuring text.
      */
     public void setTextScaleX(float scaleX) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetTextScaleX(mNativePaint, scaleX);
     }
 
@@ -1486,7 +1452,6 @@
      * @return         the paint's skew factor in X for drawing text.
      */
     public float getTextSkewX() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetTextSkewX(mNativePaint);
     }
 
@@ -1499,7 +1464,6 @@
      * @param skewX set the paint's skew factor in X for drawing text.
      */
     public void setTextSkewX(float skewX) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetTextSkewX(mNativePaint, skewX);
     }
 
@@ -1512,7 +1476,6 @@
      * @return         the paint's letter-spacing for drawing text.
      */
     public float getLetterSpacing() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetLetterSpacing(mNativePaint);
     }
 
@@ -1524,7 +1487,6 @@
      * @param letterSpacing set the paint's letter-spacing for drawing text.
      */
     public void setLetterSpacing(float letterSpacing) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetLetterSpacing(mNativePaint, letterSpacing);
     }
 
@@ -1546,7 +1508,6 @@
      * @param settings the font feature settings string to use, may be null.
      */
     public void setFontFeatureSettings(String settings) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (settings != null && settings.equals("")) {
             settings = null;
         }
@@ -1566,7 +1527,6 @@
      * @hide
      */
     public int getHyphenEdit() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetHyphenEdit(mNativePaint);
     }
 
@@ -1579,7 +1539,6 @@
      * @hide
      */
     public void setHyphenEdit(int hyphen) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         nSetHyphenEdit(mNativePaint, hyphen);
     }
 
@@ -1591,7 +1550,6 @@
      *         current typeface and text size.
      */
     public float ascent() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nAscent(mNativePaint, mNativeTypeface);
     }
 
@@ -1605,7 +1563,6 @@
      *         the current typeface and text size.
      */
     public float descent() {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nDescent(mNativePaint, mNativeTypeface);
     }
 
@@ -1652,7 +1609,6 @@
      * @return the font's recommended interline spacing.
      */
     public float getFontMetrics(FontMetrics metrics) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
     }
 
@@ -1698,7 +1654,6 @@
      * @return the font's interline spacing.
      */
     public int getFontMetricsInt(FontMetricsInt fmi) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
     }
 
@@ -1731,7 +1686,6 @@
      * @return      The width of the text
      */
     public float measureText(char[] text, int index, int count) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1764,7 +1718,6 @@
      * @return      The width of the text
      */
     public float measureText(String text, int start, int end) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1794,7 +1747,6 @@
      * @return      The width of the text
      */
     public float measureText(String text) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1855,7 +1807,6 @@
      */
     public int breakText(char[] text, int index, int count,
                                 float maxWidth, float[] measuredWidth) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1903,7 +1854,6 @@
     public int breakText(CharSequence text, int start, int end,
                          boolean measureForwards,
                          float maxWidth, float[] measuredWidth) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1952,7 +1902,6 @@
      */
     public int breakText(String text, boolean measureForwards,
                                 float maxWidth, float[] measuredWidth) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1990,7 +1939,6 @@
      */
     public int getTextWidths(char[] text, int index, int count,
                              float[] widths) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2074,7 +2022,6 @@
      * @return       the number of code units in the specified text.
      */
     public int getTextWidths(String text, int start, int end, float[] widths) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2128,7 +2075,6 @@
             int contextIndex, int contextCount, boolean isRtl, float[] advances,
             int advancesIndex) {
 
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (chars == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2175,7 +2121,6 @@
     public float getTextRunAdvances(CharSequence text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float[] advances,
             int advancesIndex) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2257,7 +2202,6 @@
      */
     public float getTextRunAdvances(String text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2322,7 +2266,6 @@
      */
     public int getTextRunCursor(char[] text, int contextStart, int contextLength,
             int dir, int offset, int cursorOpt) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         int contextEnd = contextStart + contextLength;
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
@@ -2410,7 +2353,6 @@
      */
     public int getTextRunCursor(String text, int contextStart, int contextEnd,
             int dir, int offset, int cursorOpt) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
                 | (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2437,7 +2379,6 @@
      */
     public void getTextPath(char[] text, int index, int count,
                             float x, float y, Path path) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
@@ -2460,7 +2401,6 @@
      */
     public void getTextPath(String text, int start, int end,
                             float x, float y, Path path) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
@@ -2479,7 +2419,6 @@
      *               allocated by the caller.
      */
     public void getTextBounds(String text, int start, int end, Rect bounds) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
@@ -2500,7 +2439,6 @@
      *               allocated by the caller.
      */
     public void getTextBounds(char[] text, int index, int count, Rect bounds) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
@@ -2528,7 +2466,6 @@
      * @return true if the typeface has a glyph for the string
      */
     public boolean hasGlyph(String string) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
     }
 
@@ -2570,7 +2507,6 @@
      */
     public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
             boolean isRtl, int offset) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2601,7 +2537,6 @@
      */
     public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, int offset) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2652,7 +2587,6 @@
      */
     public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float advance) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2680,7 +2614,6 @@
      */
     public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float advance) {
-        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2698,18 +2631,6 @@
         return result;
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mNativePaint != 0) {
-                nFinalizer(mNativePaint);
-                mNativePaint = 0;
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
     private static native long nInit();
     private static native long nInitWithPaint(long paint);
     private static native void nReset(long paintPtr);
@@ -2765,7 +2686,7 @@
                                 String text, int start, int end, int bidiFlags, Rect bounds);
     private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
                                 char[] text, int index, int count, int bidiFlags, Rect bounds);
-    private static native void nFinalizer(long nativePaint);
+    private static native long nGetNativeFinalizer();
 
     private static native void nSetShadowLayer(long paintPtr,
             float radius, float dx, float dy, int color);
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1857345..84ca546 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,6 +19,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.Animator.AnimatorListener;
+import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.ColorStateList;
@@ -140,6 +141,15 @@
     /** Local, mutable animator set. */
     private final AnimatorSet mAnimatorSet = new AnimatorSet();
 
+
+    private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    invalidateSelf();
+                }
+            };
+
     /**
      * The resources against which this drawable was created. Used to attempt
      * to inflate animators if applyTheme() doesn't get called.
@@ -201,9 +211,6 @@
     @Override
     public void draw(Canvas canvas) {
         mAnimatedVectorState.mVectorDrawable.draw(canvas);
-        if (isStarted()) {
-            invalidateSelf();
-        }
     }
 
     @Override
@@ -486,6 +493,7 @@
          *            animators, or {@code null} if not available
          */
         public void prepareLocalAnimators(@NonNull AnimatorSet animatorSet,
+                @NonNull ValueAnimator.AnimatorUpdateListener updateListener,
                 @Nullable Resources res) {
             // Check for uninflated animators. We can remove this after we add
             // support for Animator.applyTheme(). See comments in inflate().
@@ -511,6 +519,17 @@
                     final Animator nextAnim = prepareLocalAnimator(i);
                     builder.with(nextAnim);
                 }
+
+                // Setup a value animator to get animation update callbacks.
+                long totalDuration = animatorSet.getTotalDuration();
+                ValueAnimator updateAnim = ValueAnimator.ofFloat(0f, 1f);
+                if (totalDuration == ValueAnimator.DURATION_INFINITE) {
+                    updateAnim.setRepeatCount(ValueAnimator.INFINITE);
+                } else {
+                    updateAnim.setDuration(totalDuration);
+                }
+                updateAnim.addUpdateListener(updateListener);
+                builder.with(updateAnim);
             }
         }
 
@@ -603,7 +622,7 @@
     @NonNull
     private void ensureAnimatorSet() {
         if (!mHasAnimatorSet) {
-            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
+            mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mUpdateListener, mRes);
             mHasAnimatorSet = true;
             mRes = null;
         }
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 651b453..e2150c0 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -82,8 +82,13 @@
      */
     public static final int PADDING_MODE_STACK = 1;
 
-    /** Value used for undefined start and end insets. */
-    private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
+    /**
+     * Value used for undefined start and end insets.
+     *
+     * @see #getLayerInsetStart(int)
+     * @see #getLayerInsetEnd(int)
+     */
+    public static final int UNDEFINED_INSET = Integer.MIN_VALUE;
 
     LayerState mLayerState;
 
@@ -867,7 +872,8 @@
 
     /**
      * @param index the index of the layer
-     * @return number of pixels to inset from the start bound
+     * @return the number of pixels to inset from the start bound, or
+     *         {@link #UNDEFINED_INSET} if not specified
      * @attr ref android.R.styleable#LayerDrawableItem_start
      */
     public int getLayerInsetStart(int index) {
@@ -877,7 +883,8 @@
 
     /**
      * @param index the index of the layer to adjust
-     * @param e number of pixels to inset from the end bound
+     * @param e number of pixels to inset from the end bound, or
+     *         {@link #UNDEFINED_INSET} if not specified
      * @attr ref android.R.styleable#LayerDrawableItem_end
      */
     public void setLayerInsetEnd(int index, int e) {
@@ -977,34 +984,33 @@
             computeStackedPadding(padding);
         }
 
+        final int paddingT = layerState.mPaddingTop;
+        final int paddingB = layerState.mPaddingBottom;
+
+        // Resolve padding for RTL. Relative padding overrides absolute
+        // padding.
+        final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
+        final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart;
+        final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd;
+        final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft;
+        final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight;
+
         // If padding was explicitly specified (e.g. not -1) then override the
         // computed padding in that dimension.
-        if (layerState.mPaddingTop >= 0) {
-            padding.top = layerState.mPaddingTop;
+        if (paddingL >= 0) {
+            padding.left = paddingL;
         }
 
-        if (layerState.mPaddingBottom >= 0) {
-            padding.bottom = layerState.mPaddingBottom;
+        if (paddingT >= 0) {
+            padding.top = paddingT;
         }
 
-        final int paddingRtlLeft;
-        final int paddingRtlRight;
-        if (getLayoutDirection() == LayoutDirection.RTL) {
-            paddingRtlLeft = layerState.mPaddingEnd;
-            paddingRtlRight = layerState.mPaddingStart;
-        } else {
-            paddingRtlLeft = layerState.mPaddingStart;
-            paddingRtlRight = layerState.mPaddingEnd;
+        if (paddingR >= 0) {
+            padding.right = paddingR;
         }
 
-        final int paddingLeft =  paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
-        if (paddingLeft >= 0) {
-            padding.left = paddingLeft;
-        }
-
-        final int paddingRight =  paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
-        if (paddingRight >= 0) {
-            padding.right = paddingRight;
+        if (paddingB >= 0) {
+            padding.bottom = paddingB;
         }
 
         return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
@@ -1471,58 +1477,59 @@
     }
 
     private void updateLayerBounds(Rect bounds) {
-        int padL = 0;
-        int padT = 0;
-        int padR = 0;
-        int padB = 0;
+        int paddingL = 0;
+        int paddingT = 0;
+        int paddingR = 0;
+        int paddingB = 0;
 
         final Rect outRect = mTmpOutRect;
         final int layoutDirection = getLayoutDirection();
-        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
+        final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL;
+        final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST;
         final ChildDrawable[] array = mLayerState.mChildren;
-        final int N = mLayerState.mNum;
-        for (int i = 0; i < N; i++) {
+
+        for (int i = 0, count = mLayerState.mNum; i < count; i++) {
             final ChildDrawable r = array[i];
             final Drawable d = r.mDrawable;
             if (d == null) {
                 continue;
             }
 
-            final Rect container = mTmpContainer;
-            container.set(d.getBounds());
+            final int insetT = r.mInsetT;
+            final int insetB = r.mInsetB;
 
-            // Take the resolved layout direction into account. If start / end
-            // padding are defined, they will be resolved (hence overriding) to
-            // left / right or right / left depending on the resolved layout
-            // direction. If start / end padding are not defined, use the
-            // left / right ones.
-            final int insetL, insetR;
-            if (layoutDirection == LayoutDirection.RTL) {
-                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
-                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
-            } else {
-                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
-                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
-            }
+            // Resolve insets for RTL. Relative insets override absolute
+            // insets.
+            final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
+            final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
+            final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL;
+            final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR;
 
             // Establish containing region based on aggregate padding and
             // requested insets for the current layer.
-            container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
-                    bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
+            final Rect container = mTmpContainer;
+            container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT,
+                    bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB);
 
-            // Apply resolved gravity to drawable based on resolved size.
-            final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight,
-                    d.getIntrinsicWidth(), d.getIntrinsicHeight());
-            final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
-            final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
-            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
+            // Compute a reasonable default gravity based on the intrinsic and
+            // explicit dimensions, if specified.
+            final int intrinsicW = d.getIntrinsicWidth();
+            final int intrinsicH = d.getIntrinsicHeight();
+            final int layerW = r.mWidth;
+            final int layerH = r.mHeight;
+            final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH);
+
+            // Explicit dimensions override intrinsic dimensions.
+            final int resolvedW = layerW < 0 ? intrinsicW : layerW;
+            final int resolvedH = layerH < 0 ? intrinsicH : layerH;
+            Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection);
             d.setBounds(outRect);
 
-            if (nest) {
-                padL += mPaddingL[i];
-                padR += mPaddingR[i];
-                padT += mPaddingT[i];
-                padB += mPaddingB[i];
+            if (isPaddingNested) {
+                paddingL += mPaddingL[i];
+                paddingR += mPaddingR[i];
+                paddingT += mPaddingT[i];
+                paddingB += mPaddingB[i];
             }
         }
     }
@@ -1578,6 +1585,7 @@
         int padR = 0;
 
         final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
+        final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
         final ChildDrawable[] array = mLayerState.mChildren;
         final int N = mLayerState.mNum;
         for (int i = 0; i < N; i++) {
@@ -1591,15 +1599,10 @@
             // left / right or right / left depending on the resolved layout
             // direction. If start / end padding are not defined, use the
             // left / right ones.
-            final int insetL, insetR;
-            final int layoutDirection = getLayoutDirection();
-            if (layoutDirection == LayoutDirection.RTL) {
-                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
-                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
-            } else {
-                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
-                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
-            }
+            final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
+            final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
+            final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL;
+            final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR;
 
             // Don't apply padding and insets for children that don't have
             // an intrinsic dimension.
@@ -1659,8 +1662,8 @@
         if (r.mDrawable != null) {
             final Rect rect = mTmpRect;
             r.mDrawable.getPadding(rect);
-            if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] ||
-                    rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
+            if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i]
+                    || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
                 mPaddingL[i] = rect.left;
                 mPaddingT[i] = rect.top;
                 mPaddingR[i] = rect.right;
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index d313aa5..6bd2646 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -33,6 +33,7 @@
  * Draws a ripple background.
  */
 class RippleBackground extends RippleComponent {
+
     private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
 
     private static final int OPACITY_ENTER_DURATION = 600;
@@ -48,8 +49,14 @@
     // Software rendering properties.
     private float mOpacity = 0;
 
-    public RippleBackground(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+    /** Whether this ripple is bounded. */
+    private boolean mIsBounded;
+
+    public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded,
+            boolean forceSoftware) {
         super(owner, bounds, forceSoftware);
+
+        mIsBounded = isBounded;
     }
 
     public boolean isVisible() {
@@ -105,7 +112,8 @@
         final AnimatorSet.Builder builder = set.play(exit);
 
         // Linear "fast" enter based on current opacity.
-        final int fastEnterDuration = (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST);
+        final int fastEnterDuration = mIsBounded ?
+                (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
         if (fastEnterDuration > 0) {
             final ObjectAnimator enter = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 1);
             enter.setInterpolator(LINEAR_INTERPOLATOR);
@@ -131,15 +139,18 @@
         mPropX = CanvasProperty.createFloat(0);
         mPropY = CanvasProperty.createFloat(0);
 
-        final int fastEnterDuration = (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST);
+        final int fastEnterDuration = mIsBounded ?
+                (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0;
 
         // Linear exit after enter is completed.
         final RenderNodeAnimator exit = new RenderNodeAnimator(
                 mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0);
         exit.setInterpolator(LINEAR_INTERPOLATOR);
         exit.setDuration(OPACITY_EXIT_DURATION);
-        exit.setStartDelay(fastEnterDuration);
-        exit.setStartValue(targetAlpha);
+        if (fastEnterDuration > 0) {
+            exit.setStartDelay(fastEnterDuration);
+            exit.setStartValue(targetAlpha);
+        }
         set.add(exit);
 
         // Linear "fast" enter based on current opacity.
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 5213e10..9c691e5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -540,7 +540,8 @@
      */
     private void tryBackgroundEnter(boolean focused) {
         if (mBackground == null) {
-            mBackground = new RippleBackground(this, mHotspotBounds, mForceSoftware);
+            final boolean isBounded = isBounded();
+            mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware);
         }
 
         mBackground.setup(mState.mMaxRadius, mDensity);
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 78424e3..c0dfe77 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -204,11 +204,13 @@
 
     /**
      * Sets the X position around which the drawable is rotated.
+     * <p>
+     * If the X pivot is relative (as specified by
+     * {@link #setPivotXRelative(boolean)}), then the position represents a
+     * fraction of the drawable width. Otherwise, the position represents an
+     * absolute value in pixels.
      *
-     * @param pivotX X position around which to rotate. If the X pivot is
-     *            relative, the position represents a fraction of the drawable
-     *            width. Otherwise, the position represents an absolute value in
-     *            pixels.
+     * @param pivotX X position around which to rotate
      * @see #setPivotXRelative(boolean)
      * @attr ref android.R.styleable#RotateDrawable_pivotX
      */
@@ -254,11 +256,13 @@
 
     /**
      * Sets the Y position around which the drawable is rotated.
+     * <p>
+     * If the Y pivot is relative (as specified by
+     * {@link #setPivotYRelative(boolean)}), then the position represents a
+     * fraction of the drawable height. Otherwise, the position represents an
+     * absolute value in pixels.
      *
-     * @param pivotY Y position around which to rotate. If the Y pivot is
-     *            relative, the position represents a fraction of the drawable
-     *            height. Otherwise, the position represents an absolute value
-     *            in pixels.
+     * @param pivotY Y position around which to rotate
      * @see #getPivotY()
      * @attr ref android.R.styleable#RotateDrawable_pivotY
      */
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 3d4e47d..914ac3d 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -100,16 +100,17 @@
      * then on success, *cookie is set to the value corresponding to the
      * newly-added asset source.
      */
-    bool addAssetPath(const String8& path, int32_t* cookie, bool appAsLib=false);
+    bool addAssetPath(const String8& path, int32_t* cookie,
+        bool appAsLib=false, bool isSystemAsset=false);
     bool addOverlayPath(const String8& path, int32_t* cookie);
 
-    /*                                                                       
+    /*
      * Convenience for adding the standard system assets.  Uses the
      * ANDROID_ROOT environment variable to find them.
      */
     bool addDefaultAssets();
 
-    /*                                                                       
+    /*
      * Iterate over the asset paths in this manager.  (Previously
      * added via addAssetPath() and addDefaultAssets().)  On first call,
      * 'cookie' must be 0, resulting in the first cookie being returned.
@@ -118,7 +119,7 @@
      */
     int32_t nextAssetPath(const int32_t cookie) const;
 
-    /*                                                                       
+    /*
      * Return an asset path in the manager.  'which' must be between 0 and
      * countAssetPaths().
      */
@@ -221,11 +222,11 @@
      * the current data.
      */
     bool isUpToDate();
-    
+
     /**
      * Get the known locales for this asset manager object.
      */
-    void getLocales(Vector<String8>* locales) const;
+    void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const;
 
     /**
      * Generate idmap data to translate resources IDs between a package and a
@@ -237,11 +238,13 @@
 private:
     struct asset_path
     {
-        asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemOverlay(false) {}
+        asset_path() : path(""), type(kFileTypeRegular), idmap(""),
+                       isSystemOverlay(false), isSystemAsset(false) {}
         String8 path;
         FileType type;
         String8 idmap;
         bool isSystemOverlay;
+        bool isSystemAsset;
     };
 
     Asset* openInPathLocked(const char* fileName, AccessMode mode,
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 49b6333..428a2b8 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1552,9 +1552,9 @@
 
     status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false);
     status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false,
-            bool appAsLib=false);
+            bool appAsLib=false, bool isSystemAsset=false);
 
-    status_t add(ResTable* src);
+    status_t add(ResTable* src, bool isSystemAsset=false);
     status_t addEmpty(const int32_t cookie);
 
     status_t getError() const;
@@ -1822,9 +1822,9 @@
 
     // Return the configurations (ResTable_config) that we know about
     void getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap=false,
-            bool ignoreAndroidPackage=false) const;
+            bool ignoreAndroidPackage=false, bool includeSystemConfigs=true) const;
 
-    void getLocales(Vector<String8>* locales) const;
+    void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const;
 
     // Generate an idmap.
     //
@@ -1860,7 +1860,7 @@
     typedef Vector<Type*> TypeList;
 
     status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
-            bool appAsLib, const int32_t cookie, bool copyData);
+            bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
 
     ssize_t getResourcePackageIndex(uint32_t resID) const;
 
@@ -1873,10 +1873,11 @@
             size_t nameLen, uint32_t* outTypeSpecFlags) const;
 
     status_t parsePackage(
-        const ResTable_package* const pkg, const Header* const header, bool appAsLib);
+        const ResTable_package* const pkg, const Header* const header,
+        bool appAsLib, bool isSystemAsset);
 
     void print_value(const Package* pkg, const Res_value& value) const;
-    
+
     mutable Mutex               mLock;
 
     status_t                    mError;
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 8a03b94..6913f43 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -176,7 +176,8 @@
     delete[] mVendor;
 }
 
-bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAsLib)
+bool AssetManager::addAssetPath(
+        const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset)
 {
     AutoMutex _l(mLock);
 
@@ -222,6 +223,7 @@
     }
     delete manifestAsset;
 
+    ap.isSystemAsset = isSystemAsset;
     mAssetPaths.add(ap);
 
     // new paths are always added at the end
@@ -233,6 +235,7 @@
     // Load overlays, if any
     asset_path oap;
     for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
+        oap.isSystemAsset = isSystemAsset;
         mAssetPaths.add(oap);
     }
 #endif
@@ -340,7 +343,7 @@
     String8 path(root);
     path.appendPath(kSystemAssets);
 
-    return addAssetPath(path, NULL);
+    return addAssetPath(path, NULL, false /* appAsLib */, true /* isSystemAsset */);
 }
 
 int32_t AssetManager::nextAssetPath(const int32_t cookie) const
@@ -682,10 +685,10 @@
         ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
         if (sharedRes != NULL) {
             ALOGV("Copying existing resources for %s", ap.path.string());
-            mResources->add(sharedRes);
+            mResources->add(sharedRes, ap.isSystemAsset);
         } else {
             ALOGV("Parsing resources for %s", ap.path.string());
-            mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib);
+            mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib, ap.isSystemAsset);
         }
         onlyEmptyResources = false;
 
@@ -831,11 +834,11 @@
     return mZipSet.isUpToDate();
 }
 
-void AssetManager::getLocales(Vector<String8>* locales) const
+void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
 {
     ResTable* res = mResources;
     if (res != NULL) {
-        res->getLocales(locales);
+        res->getLocales(locales, includeSystemLocales);
     }
 
     const size_t numLocales = locales->size();
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 21b543e..44f92c7 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3080,13 +3080,16 @@
 // table that defined the package); the ones after are skins on top of it.
 struct ResTable::PackageGroup
 {
-    PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id, bool appAsLib)
+    PackageGroup(
+            ResTable* _owner, const String16& _name, uint32_t _id,
+            bool appAsLib, bool _isSystemAsset)
         : owner(_owner)
         , name(_name)
         , id(_id)
         , largestTypeId(0)
         , bags(NULL)
         , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
+        , isSystemAsset(_isSystemAsset)
     { }
 
     ~PackageGroup() {
@@ -3178,6 +3181,10 @@
     // by having these tables in a per-package scope rather than
     // per-package-group.
     DynamicRefTable                 dynamicRefTable;
+
+    // If the package group comes from a system asset. Used in
+    // determining non-system locales.
+    const bool                      isSystemAsset;
 };
 
 struct ResTable::bag_set
@@ -3572,8 +3579,9 @@
             copyData);
 }
 
-status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
-        bool appAsLib) {
+status_t ResTable::add(
+        Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
+        bool appAsLib, bool isSystemAsset) {
     const void* data = asset->getBuffer(true);
     if (data == NULL) {
         ALOGW("Unable to get buffer of resource asset file");
@@ -3592,20 +3600,21 @@
     }
 
     return addInternal(data, static_cast<size_t>(asset->getLength()),
-            idmapData, idmapSize, appAsLib, cookie, copyData);
+            idmapData, idmapSize, appAsLib, cookie, copyData, isSystemAsset);
 }
 
-status_t ResTable::add(ResTable* src)
+status_t ResTable::add(ResTable* src, bool isSystemAsset)
 {
     mError = src->mError;
 
-    for (size_t i=0; i<src->mHeaders.size(); i++) {
+    for (size_t i=0; i < src->mHeaders.size(); i++) {
         mHeaders.add(src->mHeaders[i]);
     }
 
-    for (size_t i=0; i<src->mPackageGroups.size(); i++) {
+    for (size_t i=0; i < src->mPackageGroups.size(); i++) {
         PackageGroup* srcPg = src->mPackageGroups[i];
-        PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id, false);
+        PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id,
+                false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset);
         for (size_t j=0; j<srcPg->packages.size(); j++) {
             pg->packages.add(srcPg->packages[j]);
         }
@@ -3646,7 +3655,7 @@
 }
 
 status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
-        bool appAsLib, const int32_t cookie, bool copyData)
+        bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset)
 {
     if (!data) {
         return NO_ERROR;
@@ -3749,7 +3758,8 @@
                 return (mError=BAD_TYPE);
             }
 
-            if (parsePackage((ResTable_package*)chunk, header, appAsLib) != NO_ERROR) {
+            if (parsePackage(
+                    (ResTable_package*)chunk, header, appAsLib, isSystemAsset) != NO_ERROR) {
                 return mError;
             }
             curPackage++;
@@ -5663,7 +5673,7 @@
 }
 
 void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
-        bool ignoreAndroidPackage) const {
+        bool ignoreAndroidPackage, bool includeSystemConfigs) const {
     const size_t packageCount = mPackageGroups.size();
     String16 android("android");
     for (size_t i = 0; i < packageCount; i++) {
@@ -5671,6 +5681,9 @@
         if (ignoreAndroidPackage && android == packageGroup->name) {
             continue;
         }
+        if (!includeSystemConfigs && packageGroup->isSystemAsset) {
+            continue;
+        }
         const size_t typeCount = packageGroup->types.size();
         for (size_t j = 0; j < typeCount; j++) {
             const TypeList& typeList = packageGroup->types[j];
@@ -5707,11 +5720,14 @@
     }
 }
 
-void ResTable::getLocales(Vector<String8>* locales) const
+void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const
 {
     Vector<ResTable_config> configs;
     ALOGV("calling getConfigurations");
-    getConfigurations(&configs);
+    getConfigurations(&configs,
+            false /* ignoreMipmap */,
+            false /* ignoreAndroidPackage */,
+            includeSystemLocales /* includeSystemConfigs */);
     ALOGV("called getConfigurations size=%d", (int)configs.size());
     const size_t I = configs.size();
 
@@ -5937,7 +5953,7 @@
 }
 
 status_t ResTable::parsePackage(const ResTable_package* const pkg,
-                                const Header* const header, bool appAsLib)
+                                const Header* const header, bool appAsLib, bool isSystemAsset)
 {
     const uint8_t* base = (const uint8_t*)pkg;
     status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
@@ -5985,8 +6001,8 @@
     if (id >= 256) {
         LOG_ALWAYS_FATAL("Package id out of range");
         return NO_ERROR;
-    } else if (id == 0 || appAsLib) {
-        // This is a library so assign an ID
+    } else if (id == 0 || appAsLib || isSystemAsset) {
+        // This is a library or a system asset, so assign an ID
         id = mNextPackageId++;
     }
 
@@ -6018,7 +6034,7 @@
 
         char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
         strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
-        group = new PackageGroup(this, String16(tmpName), id, appAsLib);
+        group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset);
         if (group == NULL) {
             delete package;
             return (mError=NO_MEMORY);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index f48c509..fa7c8aa 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
-HWUI_NEW_OPS := false
+HWUI_NEW_OPS := true
 
 hwui_src_files := \
     font/CacheTexture.cpp \
@@ -53,6 +53,7 @@
     FrameInfoVisualizer.cpp \
     GammaFontRenderer.cpp \
     GlopBuilder.cpp \
+    GpuMemoryTracker.cpp \
     GradientCache.cpp \
     Image.cpp \
     Interpolator.cpp \
@@ -108,7 +109,9 @@
     hwui_src_files += \
         BakedOpDispatcher.cpp \
         BakedOpRenderer.cpp \
-        OpReorderer.cpp \
+        BakedOpState.cpp \
+        FrameBuilder.cpp \
+        LayerBuilder.cpp \
         RecordingCanvas.cpp
 
     hwui_cflags += -DHWUI_NEW_OPS
@@ -223,11 +226,14 @@
     $(hwui_test_common_src_files) \
     tests/unit/CanvasStateTests.cpp \
     tests/unit/ClipAreaTests.cpp \
+    tests/unit/CrashHandlerInjector.cpp \
     tests/unit/DamageAccumulatorTests.cpp \
     tests/unit/DeviceInfoTests.cpp \
     tests/unit/FatVectorTests.cpp \
+    tests/unit/GpuMemoryTrackerTests.cpp \
     tests/unit/LayerUpdateQueueTests.cpp \
     tests/unit/LinearAllocatorTests.cpp \
+    tests/unit/LeakCheckTests.cpp \
     tests/unit/VectorDrawableTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
     tests/unit/StringUtilsTests.cpp
@@ -235,8 +241,8 @@
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
         tests/unit/BakedOpStateTests.cpp \
-        tests/unit/RecordingCanvasTests.cpp \
-        tests/unit/OpReordererTests.cpp
+        tests/unit/FrameBuilderTests.cpp \
+        tests/unit/RecordingCanvasTests.cpp
 endif
 
 include $(BUILD_NATIVE_TEST)
@@ -297,7 +303,7 @@
 
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
-        tests/microbench/OpReordererBench.cpp
+        tests/microbench/FrameBuilderBench.cpp
 endif
 
 include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 41411a9..6afff1b 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -39,40 +39,22 @@
         if (!mTexture) {
             Caches& caches = Caches::getInstance();
             mTexture = new Texture(caches);
-            mTexture->width = buffer->getWidth();
-            mTexture->height = buffer->getHeight();
+            mTexture->wrap(mImage->getTexture(),
+                    buffer->getWidth(), buffer->getHeight(), GL_RGBA);
             createEntries(caches, map, count);
         }
     } else {
         ALOGW("Could not create atlas image");
-        delete mImage;
-        mImage = nullptr;
+        terminate();
     }
-
-    updateTextureId();
 }
 
 void AssetAtlas::terminate() {
-    if (mImage) {
-        delete mImage;
-        mImage = nullptr;
-        updateTextureId();
-    }
-}
-
-
-void AssetAtlas::updateTextureId() {
-    mTexture->id = mImage ? mImage->getTexture() : 0;
-    if (mTexture->id) {
-        // Texture ID changed, force-set to defaults to sync the wrapper & GL
-        // state objects
-        mTexture->setWrap(GL_CLAMP_TO_EDGE, false, true);
-        mTexture->setFilter(GL_NEAREST, false, true);
-    }
-    for (size_t i = 0; i < mEntries.size(); i++) {
-        AssetAtlas::Entry* entry = mEntries.valueAt(i);
-        entry->texture->id = mTexture->id;
-    }
+    delete mImage;
+    mImage = nullptr;
+    delete mTexture;
+    mTexture = nullptr;
+    mEntries.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -80,13 +62,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const {
-    ssize_t index = mEntries.indexOfKey(pixelRef);
-    return index >= 0 ? mEntries.valueAt(index) : nullptr;
+    auto result = mEntries.find(pixelRef);
+    return result != mEntries.end() ? result->second.get() : nullptr;
 }
 
 Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const {
-    ssize_t index = mEntries.indexOfKey(pixelRef);
-    return index >= 0 ? mEntries.valueAt(index)->texture : nullptr;
+    auto result = mEntries.find(pixelRef);
+    return result != mEntries.end() ? result->second->texture : nullptr;
 }
 
 /**
@@ -94,7 +76,8 @@
  * instead of applying the changes to the virtual textures.
  */
 struct DelegateTexture: public Texture {
-    DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { }
+    DelegateTexture(Caches& caches, Texture* delegate)
+            : Texture(caches), mDelegate(delegate) { }
 
     virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
             bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override {
@@ -111,8 +94,8 @@
 }; // struct DelegateTexture
 
 void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) {
-    const float width = float(mTexture->width);
-    const float height = float(mTexture->height);
+    const float width = float(mTexture->width());
+    const float height = float(mTexture->height());
 
     for (int i = 0; i < count; ) {
         SkPixelRef* pixelRef = reinterpret_cast<SkPixelRef*>(map[i++]);
@@ -133,13 +116,13 @@
 
         Texture* texture = new DelegateTexture(caches, mTexture);
         texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType());
-        texture->width = pixelRef->info().width();
-        texture->height = pixelRef->info().height();
+        texture->wrap(mTexture->id(), pixelRef->info().width(),
+                pixelRef->info().height(), mTexture->format());
 
-        Entry* entry = new Entry(pixelRef, texture, mapper, *this);
+        std::unique_ptr<Entry> entry(new Entry(pixelRef, texture, mapper, *this));
         texture->uvMapper = &entry->uvMapper;
 
-        mEntries.add(entry->pixelRef, entry);
+        mEntries.emplace(entry->pixelRef, std::move(entry));
     }
 }
 
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index a037725..75400ff 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -17,19 +17,17 @@
 #ifndef ANDROID_HWUI_ASSET_ATLAS_H
 #define ANDROID_HWUI_ASSET_ATLAS_H
 
-#include <GLES2/gl2.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <utils/KeyedVector.h>
-
-#include <cutils/compiler.h>
-
-#include <SkBitmap.h>
-
 #include "Texture.h"
 #include "UvMapper.h"
 
+#include <cutils/compiler.h>
+#include <GLES2/gl2.h>
+#include <ui/GraphicBuffer.h>
+#include <SkBitmap.h>
+
+#include <memory>
+#include <unordered_map>
+
 namespace android {
 namespace uirenderer {
 
@@ -71,6 +69,10 @@
             return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey;
         }
 
+        ~Entry() {
+            delete texture;
+        }
+
     private:
         /**
          * The pixel ref that generated this atlas entry.
@@ -90,10 +92,6 @@
                 , atlas(atlas) {
         }
 
-        ~Entry() {
-            delete texture;
-        }
-
         friend class AssetAtlas;
     };
 
@@ -127,7 +125,7 @@
      * Can return 0 if the atlas is not initialized.
      */
     uint32_t getWidth() const {
-        return mTexture ? mTexture->width : 0;
+        return mTexture ? mTexture->width() : 0;
     }
 
     /**
@@ -135,7 +133,7 @@
      * Can return 0 if the atlas is not initialized.
      */
     uint32_t getHeight() const {
-        return mTexture ? mTexture->height : 0;
+        return mTexture ? mTexture->height() : 0;
     }
 
     /**
@@ -143,7 +141,7 @@
      * Can return 0 if the atlas is not initialized.
      */
     GLuint getTexture() const {
-        return mTexture ? mTexture->id : 0;
+        return mTexture ? mTexture->id() : 0;
     }
 
     /**
@@ -160,7 +158,6 @@
 
 private:
     void createEntries(Caches& caches, int64_t* map, int count);
-    void updateTextureId();
 
     Texture* mTexture;
     Image* mImage;
@@ -168,7 +165,7 @@
     const bool mBlendKey;
     const bool mOpaqueKey;
 
-    KeyedVector<const SkPixelRef*, Entry*> mEntries;
+    std::unordered_map<const SkPixelRef*, std::unique_ptr<Entry>> mEntries;
 }; // class AssetAtlas
 
 }; // namespace uirenderer
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 0f0768f..7ecc743 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -79,7 +79,9 @@
             .setTransform(Matrix4::identity(), TransformFlags::None)
             .setModelViewIdentityEmptyBounds()
             .build();
-    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
+    ClipRect renderTargetClip(opList.clip);
+    const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
+    renderer.renderGlop(nullptr, clip, glop);
 }
 
 void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
@@ -183,7 +185,9 @@
             .setTransform(Matrix4::identity(), TransformFlags::None)
             .setModelViewIdentityEmptyBounds()
             .build();
-    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
+    ClipRect renderTargetClip(opList.clip);
+    const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
+    renderer.renderGlop(nullptr, clip, glop);
 }
 
 static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
@@ -213,7 +217,7 @@
             .setMeshTexturedUnitQuad(nullptr)
             .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
             .build();
     renderer.renderGlop(state, glop);
 }
@@ -224,7 +228,7 @@
 };
 
 static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
-        const Rect* renderClip, TextRenderType renderType) {
+        const ClipBase* renderClip, TextRenderType renderType) {
     FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
 
     if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) {
@@ -272,7 +276,7 @@
 
     bool forceFinish = (renderType == TextRenderType::Flush);
     bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
-    const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect : nullptr;
+    const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
     fontRenderer.renderPosText(op.paint, localOpClip,
             (const char*) op.glyphs, op.glyphCount, x, y,
             op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
@@ -287,7 +291,8 @@
 
 void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
         const MergedBakedOpList& opList) {
-    const Rect* clip = opList.clipSideFlags ? &opList.clip : nullptr;
+    ClipRect renderTargetClip(opList.clip);
+    const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
     for (size_t i = 0; i < opList.count; i++) {
         const BakedOpState& state = *(opList.states[i]);
         const TextOp& op = *(static_cast<const TextOp*>(state.op));
@@ -297,26 +302,6 @@
     }
 }
 
-void BakedOpDispatcher::onRenderNodeOp(BakedOpRenderer&, const RenderNodeOp&, const BakedOpState&) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer&, const BeginLayerOp&, const BakedOpState&) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer&, const EndLayerOp&, const BakedOpState&) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
 namespace VertexBufferRenderFlags {
     enum {
         Offset = 0x1,
@@ -352,9 +337,9 @@
 
 static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
         PathTexture& texture, const RecordedOp& op) {
-    Rect dest(texture.width, texture.height);
-    dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
-            texture.top + op.unmappedBounds.top - texture.offset);
+    Rect dest(texture.width(), texture.height());
+    dest.translate(texture.left - texture.offset,
+            texture.top - texture.offset);
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
@@ -414,7 +399,7 @@
             .setMeshTexturedUnitQuad(texture->uvMapper)
             .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
             .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
+            .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
             .build();
     renderer.renderGlop(state, glop);
 }
@@ -498,10 +483,10 @@
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
-    Rect uv(std::max(0.0f, op.src.left / texture->width),
-            std::max(0.0f, op.src.top / texture->height),
-            std::min(1.0f, op.src.right / texture->width),
-            std::min(1.0f, op.src.bottom / texture->height));
+    Rect uv(std::max(0.0f, op.src.left / texture->width()),
+            std::max(0.0f, op.src.top / texture->height()),
+            std::min(1.0f, op.src.right / texture->width()),
+            std::min(1.0f, op.src.bottom / texture->height()));
 
     const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
@@ -560,19 +545,19 @@
             op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
 
     Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
-    if (!texture) return;
-    const AutoTexture autoCleanup(texture);
-    Glop glop;
-    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
-            .setRoundRectClipState(state.roundRectClipState)
-            .setMeshPatchQuads(*mesh)
-            .setMeshTexturedUnitQuad(texture->uvMapper)
-            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
-            .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
-                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
-            .build();
-    renderer.renderGlop(state, glop);
+    if (CC_LIKELY(texture)) {
+        const AutoTexture autoCleanup(texture);
+        Glop glop;
+        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                .setRoundRectClipState(state.roundRectClipState)
+                .setMeshPatchQuads(*mesh)
+                .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+                .setTransform(state.computedState.transform, TransformFlags::None)
+                .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+                        Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+                .build();
+        renderer.renderGlop(state, glop);
+    }
 }
 
 void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
@@ -701,14 +686,13 @@
 }
 
 void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) {
-    const Rect* clip = state.computedState.clipSideFlags ? &state.computedState.clipRect : nullptr;
-    renderTextOp(renderer, op, state, clip, TextRenderType::Flush);
+    renderTextOp(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
 }
 
 void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op, const BakedOpState& state) {
     // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
     // TODO: respect clipSideFlags, once we record with bounds
-    const Rect* renderTargetClip = &state.computedState.clipRect;
+    auto renderTargetClip = state.computedState.clipState;
 
     FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
     fontRenderer.setFont(op.paint, SkMatrix::I());
@@ -752,6 +736,7 @@
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     OffscreenBuffer* buffer = *op.layerHandle;
 
+    // Note that we don't use op->paint here - it's never set on a LayerOp
     float layerAlpha = op.alpha * state.alpha;
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -769,5 +754,38 @@
     }
 }
 
+void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
+    *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
+    LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
+}
+
+void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
+    if (!state.computedState.clippedBounds.isEmpty()) {
+        if (op.paint && op.paint->getAlpha() < 255) {
+            SkPaint layerPaint;
+            layerPaint.setAlpha(op.paint->getAlpha());
+            layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+            layerPaint.setColorFilter(op.paint->getColorFilter());
+            RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
+            BakedOpDispatcher::onRectOp(renderer, rectOp, state);
+        }
+
+        OffscreenBuffer& layer = **(op.layerHandle);
+        auto mode = PaintUtils::getXfermodeDirect(op.paint);
+        Glop glop;
+        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                .setRoundRectClipState(state.roundRectClipState)
+                .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
+                .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
+                .setTransform(state.computedState.transform, TransformFlags::None)
+                .setModelViewMapUnitToRect(state.computedState.clippedBounds)
+                .build();
+        renderer.renderGlop(state, glop);
+    }
+    renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
+}
+
 } // namespace uirenderer
 } // namespace android
diff --git a/libs/hwui/BakedOpDispatcher.h b/libs/hwui/BakedOpDispatcher.h
index ed34ada..4dfdd3f 100644
--- a/libs/hwui/BakedOpDispatcher.h
+++ b/libs/hwui/BakedOpDispatcher.h
@@ -36,13 +36,13 @@
     // Declares all "onMergedBitmapOps(...)" style methods for mergeable op types
 #define X(Type) \
         static void onMerged##Type##s(BakedOpRenderer& renderer, const MergedBakedOpList& opList);
-    MAP_MERGED_OPS(X)
+    MAP_MERGEABLE_OPS(X)
 #undef X
 
     // Declares all "onBitmapOp(...)" style methods for every op type
 #define X(Type) \
         static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state);
-    MAP_OPS(X)
+    MAP_RENDERABLE_OPS(X)
 #undef X
 
 };
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index f8282dc..4fbff0d 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -48,7 +48,7 @@
 
     // attach the texture to the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-            offscreenBuffer->texture.id, 0);
+            offscreenBuffer->texture.id(), 0);
     LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
     LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
             "framebuffer incomplete!");
@@ -60,27 +60,81 @@
 }
 
 void BakedOpRenderer::endLayer() {
+    if (mRenderTarget.stencil) {
+        // if stencil was used for clipping, detach it and return it to pool
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+        LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed");
+        mCaches.renderBufferCache.put(mRenderTarget.stencil);
+        mRenderTarget.stencil = nullptr;
+    }
+    mRenderTarget.lastStencilClip = nullptr;
+
     mRenderTarget.offscreenBuffer->updateMeshFromRegion();
-    mRenderTarget.offscreenBuffer = nullptr;
+    mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now.
 
     // Detach the texture from the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
     LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
     mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
-    mRenderTarget.frameBufferId = -1;
+    mRenderTarget.frameBufferId = 0;
+}
+
+OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) {
+    OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState,
+            area.getWidth(), area.getHeight());
+    if (!area.isEmpty()) {
+        mCaches.textureState().activateTexture(0);
+        mCaches.textureState().bindTexture(buffer->texture.id());
+
+        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+                area.left, mRenderTarget.viewportHeight - area.bottom,
+                area.getWidth(), area.getHeight());
+    }
+    return buffer;
 }
 
 void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
+    LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0");
     mRenderState.bindFramebuffer(0);
     setViewport(width, height);
-    mCaches.clearGarbage();
 
     if (!mOpaque) {
         clearColorBuffer(repaintRect);
     }
+
+    mRenderState.debugOverdraw(true, true);
 }
 
-void BakedOpRenderer::endFrame() {
+void BakedOpRenderer::endFrame(const Rect& repaintRect) {
+    if (CC_UNLIKELY(Properties::debugOverdraw)) {
+        ClipRect overdrawClip(repaintRect);
+        Rect viewportRect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight);
+        // overdraw visualization
+        for (int i = 1; i <= 4; i++) {
+            if (i < 4) {
+                // nth level of overdraw tests for n+1 draws per pixel
+                mRenderState.stencil().enableDebugTest(i + 1, false);
+            } else {
+                // 4th level tests for 4 or higher draws per pixel
+                mRenderState.stencil().enableDebugTest(4, true);
+            }
+
+            SkPaint paint;
+            paint.setColor(mCaches.getOverdrawColor(i));
+            Glop glop;
+            GlopBuilder(mRenderState, mCaches, &glop)
+                    .setRoundRectClipState(nullptr)
+                    .setMeshUnitQuad()
+                    .setFillPaint(paint, 1.0f)
+                    .setTransform(Matrix4::identity(), TransformFlags::None)
+                    .setModelViewMapUnitToRect(viewportRect)
+                    .build();
+            renderGlop(nullptr, &overdrawClip, glop);
+        }
+        mRenderState.stencil().disable();
+    }
+
+    mCaches.clearGarbage();
     mCaches.pathCache.trim();
     mCaches.tessellationCache.trim();
 
@@ -107,7 +161,7 @@
 }
 
 void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
-    if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
+    if (rect.contains(Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight))) {
         // Full viewport is being cleared - disable scissor
         mRenderState.scissor().setEnabled(false);
     } else {
@@ -128,12 +182,124 @@
     return texture;
 }
 
-void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) {
+// clears and re-fills stencil with provided rendertarget space quads,
+// and then put stencil into test mode
+void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices,
+        int incrementThreshold) {
+    mRenderState.stencil().enableWrite(incrementThreshold);
+    mRenderState.stencil().clear();
+    Glop glop;
+    GlopBuilder(mRenderState, mCaches, &glop)
+            .setRoundRectClipState(nullptr)
+            .setMeshIndexedQuads(quadVertices.data(), quadVertices.size() / 4)
+            .setFillBlack()
+            .setTransform(Matrix4::identity(), TransformFlags::None)
+            .setModelViewIdentityEmptyBounds()
+            .build();
+    mRenderState.render(glop, mRenderTarget.orthoMatrix);
+    mRenderState.stencil().enableTest(incrementThreshold);
+}
+
+void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) {
+    LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::RectangleList, "can't rectlist clip without rectlist");
+    auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
+    int quadCount = rectList.getTransformedRectanglesCount();
+    std::vector<Vertex> rectangleVertices;
+    rectangleVertices.reserve(quadCount * 4);
+    for (int i = 0; i < quadCount; i++) {
+        const TransformedRectangle& tr(rectList.getTransformedRectangle(i));
+        const Matrix4& transform = tr.getTransform();
+        Rect bounds = tr.getBounds();
+        if (transform.rectToRect()) {
+            // If rectToRect, can simply map bounds before storing verts
+            transform.mapRect(bounds);
+            bounds.doIntersect(clip->rect);
+            if (bounds.isEmpty()) {
+                continue; // will be outside of scissor, skip
+            }
+        }
+
+        rectangleVertices.push_back(Vertex{bounds.left, bounds.top});
+        rectangleVertices.push_back(Vertex{bounds.right, bounds.top});
+        rectangleVertices.push_back(Vertex{bounds.left, bounds.bottom});
+        rectangleVertices.push_back(Vertex{bounds.right, bounds.bottom});
+
+        if (!transform.rectToRect()) {
+            // If not rectToRect, must map each point individually
+            for (auto cur = rectangleVertices.end() - 4; cur < rectangleVertices.end(); cur++) {
+                transform.mapPoint(cur->x, cur->y);
+            }
+        }
+    }
+    setupStencilQuads(rectangleVertices, rectList.getTransformedRectanglesCount());
+}
+
+void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) {
+    LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::Region, "can't region clip without region");
+    auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region;
+
+    std::vector<Vertex> regionVertices;
+    SkRegion::Cliperator it(region, clip->rect.toSkIRect());
+    while (!it.done()) {
+        const SkIRect& r = it.rect();
+        regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fTop});
+        regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fTop});
+        regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fBottom});
+        regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fBottom});
+        it.next();
+    }
+    setupStencilQuads(regionVertices, 0);
+}
+
+void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) {
+    // Prepare scissor (done before stencil, to simplify filling stencil)
     mRenderState.scissor().setEnabled(clip != nullptr);
     if (clip) {
-        mRenderState.scissor().set(clip->left, mRenderTarget.viewportHeight - clip->bottom,
-            clip->getWidth(), clip->getHeight());
+        mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect);
     }
+
+    // If stencil may be used for clipping, enable it, fill it, or disable it as appropriate
+    if (CC_LIKELY(!Properties::debugOverdraw)) {
+        // only modify stencil mode and content when it's not used for overdraw visualization
+        if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) {
+            // NOTE: this pointer check is only safe for non-rect clips,
+            // since rect clips may be created on the stack
+            if (mRenderTarget.lastStencilClip != clip) {
+                // Stencil needed, but current stencil isn't up to date
+                mRenderTarget.lastStencilClip = clip;
+
+                if (mRenderTarget.frameBufferId != 0 && !mRenderTarget.stencil) {
+                    OffscreenBuffer* layer = mRenderTarget.offscreenBuffer;
+                    mRenderTarget.stencil = mCaches.renderBufferCache.get(
+                            Stencil::getLayerStencilFormat(),
+                            layer->texture.width(), layer->texture.height());
+                    // stencil is bound + allocated - associate it with current FBO
+                    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                            GL_RENDERBUFFER, mRenderTarget.stencil->getName());
+                }
+
+                if (clip->mode == ClipMode::RectangleList) {
+                    setupStencilRectList(clip);
+                } else {
+                    setupStencilRegion(clip);
+                }
+            } else {
+                // stencil is up to date - just need to ensure it's enabled (since an unclipped
+                // or scissor-only clipped op may have been drawn, disabling the stencil)
+                int incrementThreshold = 0;
+                if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
+                    auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
+                    incrementThreshold = rectList.getTransformedRectanglesCount();
+                }
+                mRenderState.stencil().enableTest(incrementThreshold);
+            }
+        } else {
+            // either scissor or no clip, so disable stencil test
+            mRenderState.stencil().disable();
+        }
+    }
+
+    // dirty offscreenbuffer
     if (dirtyBounds && mRenderTarget.offscreenBuffer) {
         // register layer damage to draw-back region
         android::Rect dirty(dirtyBounds->left, dirtyBounds->top,
@@ -142,17 +308,18 @@
     }
 }
 
-void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) {
+void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip,
+        const Glop& glop) {
     prepareRender(dirtyBounds, clip);
     mRenderState.render(glop, mRenderTarget.orthoMatrix);
     if (!mRenderTarget.frameBufferId) mHasDrawn = true;
 }
 
 void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) {
-    prepareRender(&state.computedState.clippedBounds, &state.computedState.clipRect);
+    prepareRender(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded());
 
     DrawGlInfo info;
-    auto&& clip = state.computedState.clipRect;
+    auto&& clip = state.computedState.clipRect();
     info.clipLeft = clip.left;
     info.clipTop = clip.top;
     info.clipRight = clip.right;
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index f158e8b..10c4698 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -19,6 +19,7 @@
 
 #include "BakedOpState.h"
 #include "Matrix.h"
+#include "utils/Macros.h"
 
 namespace android {
 namespace uirenderer {
@@ -27,6 +28,7 @@
 struct Glop;
 class Layer;
 class RenderState;
+struct ClipBase;
 
 /**
  * Main rendering manager for a collection of work - one frame + any contained FBOs.
@@ -43,9 +45,15 @@
      * Position agnostic shadow lighting info. Used with all shadow ops in scene.
      */
     struct LightInfo {
-        float lightRadius = 0;
-        uint8_t ambientShadowAlpha = 0;
-        uint8_t spotShadowAlpha = 0;
+        LightInfo() : LightInfo(0, 0, 0) {}
+        LightInfo(float lightRadius, uint8_t ambientShadowAlpha,
+                uint8_t spotShadowAlpha)
+                : lightRadius(lightRadius)
+                , ambientShadowAlpha(ambientShadowAlpha)
+                , spotShadowAlpha(spotShadowAlpha) {}
+        float lightRadius;
+        uint8_t ambientShadowAlpha;
+        uint8_t spotShadowAlpha;
     };
 
     BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, const LightInfo& lightInfo)
@@ -59,30 +67,33 @@
     Caches& caches() { return mCaches; }
 
     void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
-    void endFrame();
-    OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
+    void endFrame(const Rect& repaintRect);
+    WARN_UNUSED_RESULT OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
     void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
     void endLayer();
+    WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
 
     Texture* getTexture(const SkBitmap* bitmap);
     const LightInfo& getLightInfo() const { return mLightInfo; }
 
     void renderGlop(const BakedOpState& state, const Glop& glop) {
-        bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
         renderGlop(&state.computedState.clippedBounds,
-                useScissor ? &state.computedState.clipRect : nullptr,
+                state.computedState.getClipIfNeeded(),
                 glop);
     }
     void renderFunctor(const FunctorOp& op, const BakedOpState& state);
 
-    void renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop);
+    void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
     bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; }
     void dirtyRenderTarget(const Rect& dirtyRect);
     bool didDraw() const { return mHasDrawn; }
 private:
     void setViewport(uint32_t width, uint32_t height);
     void clearColorBuffer(const Rect& clearRect);
-    void prepareRender(const Rect* dirtyBounds, const Rect* clip);
+    void prepareRender(const Rect* dirtyBounds, const ClipBase* clip);
+    void setupStencilRectList(const ClipBase* clip);
+    void setupStencilRegion(const ClipBase* clip);
+    void setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold);
 
     RenderState& mRenderState;
     Caches& mCaches;
@@ -92,10 +103,23 @@
     // render target state - setup by start/end layer/frame
     // only valid to use in between start/end pairs.
     struct {
+        // If not drawing to a layer: fbo = 0, offscreenBuffer = null,
+        // Otherwise these refer to currently painting layer's state
         GLuint frameBufferId = 0;
         OffscreenBuffer* offscreenBuffer = nullptr;
+
+        // Used when drawing to a layer and using stencil clipping. otherwise null.
+        RenderBuffer* stencil = nullptr;
+
+        // value representing the ClipRectList* or ClipRegion* currently stored in
+        // the stencil of the current render target
+        const ClipBase* lastStencilClip = nullptr;
+
+        // Size of renderable region in current render target - for layers, may not match actual
+        // bounds of FBO texture. offscreenBuffer->texture has this information.
         uint32_t viewportWidth = 0;
         uint32_t viewportHeight = 0;
+
         Matrix4 orthoMatrix;
     } mRenderTarget;
 
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
new file mode 100644
index 0000000..87844f9
--- /dev/null
+++ b/libs/hwui/BakedOpState.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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 "BakedOpState.h"
+
+#include "ClipArea.h"
+
+namespace android {
+namespace uirenderer {
+
+ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
+        const RecordedOp& recordedOp, bool expandForStroke) {
+    // resolvedMatrix = parentMatrix * localMatrix
+    transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
+
+    // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+    clippedBounds = recordedOp.unmappedBounds;
+    if (CC_UNLIKELY(expandForStroke)) {
+        // account for non-hairline stroke
+        clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
+    }
+    transform.mapRect(clippedBounds);
+    if (CC_UNLIKELY(expandForStroke
+            && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
+        // account for hairline stroke when stroke may be < 1 scaled pixel
+        // Non translate || strokeWidth < 1 is conservative, but will cover all cases
+        clippedBounds.outset(0.5f);
+    }
+
+    // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+    clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator,
+            recordedOp.localClip, *(snapshot.transform));
+    LOG_ALWAYS_FATAL_IF(!clipState, "must clip!");
+
+    const Rect& clipRect = clipState->rect;
+    if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) {
+        // Rejected based on either empty clip, or bounds not intersecting with clip
+
+        // Note: we could rewind the clipState object in situations where the clipRect is empty,
+        // but *only* if the caching logic within ClipArea was aware of the rewind.
+        clipState = nullptr;
+        clippedBounds.setEmpty();
+    } else {
+        // Not rejected! compute true clippedBounds and clipSideFlags
+        if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
+        if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
+        if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
+        if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+        clippedBounds.doIntersect(clipRect);
+    }
+}
+
+ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot) {
+    transform = *snapshot.transform;
+
+    // Since the op doesn't have known bounds, we conservatively set the mapped bounds
+    // to the current clipRect, and clipSideFlags to Full.
+    clipState = snapshot.mutateClipArea().serializeClip(allocator);
+    LOG_ALWAYS_FATAL_IF(!clipState, "clipState required");
+    clippedBounds = clipState->rect;
+    clipSideFlags = OpClipSideFlags::Full;
+}
+
+ResolvedRenderState::ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect)
+        : transform(Matrix4::identity())
+        , clipState(viewportRect)
+        , clippedBounds(dstRect)
+        , clipSideFlags(OpClipSideFlags::None) {}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index b12c0c9..3db28c9 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -52,91 +52,42 @@
  */
 class ResolvedRenderState {
 public:
-    // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
-    ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) {
-        /* TODO: benchmark a fast path for translate-only matrices, such as:
-        if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
-                && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
-            float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX();
-            float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY();
-            transform.loadTranslate(translateX, translateY, 0);
+    ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
+            const RecordedOp& recordedOp, bool expandForStroke);
 
-            // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
-            clipRect = recordedOp.localClipRect;
-            clipRect.translate(translateX, translateY);
-            clipRect.doIntersect(snapshot.getClipRect());
-            clipRect.snapToPixelBoundaries();
+    // Constructor for unbounded ops without transform/clip (namely shadows)
+    ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot);
 
-            // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
-            clippedBounds = recordedOp.unmappedBounds;
-            clippedBounds.translate(translateX, translateY);
-        } ... */
-
-        // resolvedMatrix = parentMatrix * localMatrix
-        transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
-
-        // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
-        clipRect = recordedOp.localClipRect;
-        snapshot.transform->mapRect(clipRect);
-        clipRect.doIntersect(snapshot.getRenderTargetClip());
-        clipRect.snapToPixelBoundaries();
-
-        // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
-        clippedBounds = recordedOp.unmappedBounds;
-        if (CC_UNLIKELY(expandForStroke)) {
-            // account for non-hairline stroke
-            clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
-        }
-        transform.mapRect(clippedBounds);
-        if (CC_UNLIKELY(expandForStroke
-                && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
-            // account for hairline stroke when stroke may be < 1 scaled pixel
-            // Non translate || strokeWidth < 1 is conservative, but will cover all cases
-            clippedBounds.outset(0.5f);
-        }
-
-        if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
-        if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
-        if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
-        if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
-        clippedBounds.doIntersect(clipRect);
-
-        /**
-         * TODO: once we support complex clips, we may want to reject to avoid that work where
-         * possible. Should we:
-         * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp)
-         * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection
-         *         and early return null in one place.
-         */
-    }
-
-    /**
-     * Constructor for unbounded ops without transform/clip (namely shadows)
-     *
-     * Since the op doesn't have known bounds, we conservatively set the mapped bounds
-     * to the current clipRect, and clipSideFlags to Full.
-     */
-    ResolvedRenderState(const Snapshot& snapshot) {
-        transform = *snapshot.transform;
-        clipRect = snapshot.getRenderTargetClip();
-        clippedBounds = clipRect;
-        transform.mapRect(clippedBounds);
-        clipSideFlags = OpClipSideFlags::Full;
-    }
+    // Constructor for primitive ops provided clip, and no transform
+    ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect);
 
     Rect computeLocalSpaceClip() const {
         Matrix4 inverse;
         inverse.loadInverse(transform);
 
-        Rect outClip(clipRect);
+        Rect outClip(clipRect());
         inverse.mapRect(outClip);
         return outClip;
     }
 
+    const Rect& clipRect() const {
+        return clipState->rect;
+    }
+
+    bool requiresClip() const {
+        return clipSideFlags != OpClipSideFlags::None
+               || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle);
+    }
+
+    // returns the clip if it's needed to draw the operation, otherwise nullptr
+    const ClipBase* getClipIfNeeded() const {
+        return requiresClip() ? clipState : nullptr;
+    }
+
     Matrix4 transform;
-    Rect clipRect;
-    int clipSideFlags = 0;
+    const ClipBase* clipState = nullptr;
     Rect clippedBounds;
+    int clipSideFlags = 0;
 };
 
 /**
@@ -147,8 +98,10 @@
 class BakedOpState {
 public:
     static BakedOpState* tryConstruct(LinearAllocator& allocator,
-            const Snapshot& snapshot, const RecordedOp& recordedOp) {
-        BakedOpState* bakedState = new (allocator) BakedOpState(snapshot, recordedOp, false);
+            Snapshot& snapshot, const RecordedOp& recordedOp) {
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+        BakedOpState* bakedState = new (allocator) BakedOpState(
+                allocator, snapshot, recordedOp, false);
         if (bakedState->computedState.clippedBounds.isEmpty()) {
             // bounds are empty, so op is rejected
             allocator.rewindIfLastAlloc(bakedState);
@@ -165,15 +118,17 @@
     };
 
     static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
-            const Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+            Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
         bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
                 ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
                 : true;
 
         BakedOpState* bakedState = new (allocator) BakedOpState(
-                snapshot, recordedOp, expandForStroke);
+                allocator, snapshot, recordedOp, expandForStroke);
         if (bakedState->computedState.clippedBounds.isEmpty()) {
             // bounds are empty, so op is rejected
+            // NOTE: this won't succeed if a clip was allocated
             allocator.rewindIfLastAlloc(bakedState);
             return nullptr;
         }
@@ -181,11 +136,16 @@
     }
 
     static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
-            const Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
-        if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
+            Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
+        if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
 
         // clip isn't empty, so construct the op
-        return new (allocator) BakedOpState(snapshot, shadowOpPtr);
+        return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
+    }
+
+    static BakedOpState* directConstruct(LinearAllocator& allocator,
+            const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
+        return new (allocator) BakedOpState(clip, dstRect, recordedOp);
     }
 
     static void* operator new(size_t size, LinearAllocator& allocator) {
@@ -193,7 +153,7 @@
     }
 
     // computed state:
-    const ResolvedRenderState computedState;
+    ResolvedRenderState computedState;
 
     // simple state (straight pointer/value storage):
     const float alpha;
@@ -202,19 +162,27 @@
     const RecordedOp* op;
 
 private:
-    BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke)
-            : computedState(snapshot, recordedOp, expandForStroke)
+    BakedOpState(LinearAllocator& allocator, Snapshot& snapshot,
+            const RecordedOp& recordedOp, bool expandForStroke)
+            : computedState(allocator, snapshot, recordedOp, expandForStroke)
             , alpha(snapshot.alpha)
             , roundRectClipState(snapshot.roundRectClipState)
             , projectionPathMask(snapshot.projectionPathMask)
             , op(&recordedOp) {}
 
-    BakedOpState(const Snapshot& snapshot, const ShadowOp* shadowOpPtr)
-            : computedState(snapshot)
+    BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr)
+            : computedState(allocator, snapshot)
             , alpha(snapshot.alpha)
             , roundRectClipState(snapshot.roundRectClipState)
             , projectionPathMask(snapshot.projectionPathMask)
             , op(shadowOpPtr) {}
+
+    BakedOpState(const ClipRect* viewportRect, const Rect& dstRect, const RecordedOp& recordedOp)
+            : computedState(viewportRect, dstRect)
+            , alpha(1.0f)
+            , roundRectClipState(nullptr)
+            , projectionPathMask(nullptr)
+            , op(&recordedOp) {}
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index cf76e6b..cf2726b 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -45,6 +45,21 @@
     }
 }
 
+void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) {
+    if (mWidth != viewportWidth || mHeight != viewportHeight) {
+        mWidth = viewportWidth;
+        mHeight = viewportHeight;
+        mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
+        mCanvas.onViewportInitialized();
+    }
+
+    freeAllSnapshots();
+    mSnapshot = allocSnapshot(&mFirstSnapshot,
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSnapshot->setRelativeLightCenter(Vector3());
+    mSaveCount = 1;
+}
+
 void CanvasState::initializeSaveStack(
         int viewportWidth, int viewportHeight,
         float clipLeft, float clipTop,
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 4709ef4..b9e87ae 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -80,6 +80,12 @@
      * Initializes the first snapshot, computing the projection matrix,
      * and stores the dimensions of the render target.
      */
+    void initializeRecordingSaveStack(int viewportWidth, int viewportHeight);
+
+    /**
+     * Initializes the first snapshot, computing the projection matrix,
+     * and stores the dimensions of the render target.
+     */
     void initializeSaveStack(int viewportWidth, int viewportHeight,
             float clipLeft, float clipTop, float clipRight, float clipBottom,
             const Vector3& lightCenter);
@@ -168,6 +174,7 @@
     void freeAllSnapshots();
 
     /// indicates that the clip has been changed since the last time it was consumed
+    // TODO: delete when switching to HWUI_NEW_OPS
     bool mDirtyClip;
 
     /// Dimensions of the drawing surface
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 5f166ca..160090d 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -15,10 +15,11 @@
  */
 #include "ClipArea.h"
 
+#include "utils/LinearAllocator.h"
+
 #include <SkPath.h>
 #include <limits>
-
-#include "Rect.h"
+#include <type_traits>
 
 namespace android {
 namespace uirenderer {
@@ -171,12 +172,18 @@
     return rectangleListAsRegion;
 }
 
+void RectangleList::transform(const Matrix4& transform) {
+    for (int index = 0; index < mTransformedRectanglesCount; index++) {
+        mTransformedRectangles[index].transform(transform);
+    }
+}
+
 /*
  * ClipArea
  */
 
 ClipArea::ClipArea()
-        : mMode(Mode::Rectangle) {
+        : mMode(ClipMode::Rectangle) {
 }
 
 /*
@@ -184,39 +191,44 @@
  */
 
 void ClipArea::setViewportDimensions(int width, int height) {
+    mPostViewportClipObserved = false;
     mViewportBounds.set(0, 0, width, height);
     mClipRect = mViewportBounds;
 }
 
 void ClipArea::setEmpty() {
-    mMode = Mode::Rectangle;
+    onClipUpdated();
+    mMode = ClipMode::Rectangle;
     mClipRect.setEmpty();
     mClipRegion.setEmpty();
     mRectangleList.setEmpty();
 }
 
 void ClipArea::setClip(float left, float top, float right, float bottom) {
-    mMode = Mode::Rectangle;
+    onClipUpdated();
+    mMode = ClipMode::Rectangle;
     mClipRect.set(left, top, right, bottom);
     mClipRegion.setEmpty();
 }
 
 void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
         SkRegion::Op op) {
+    onClipUpdated();
     switch (mMode) {
-    case Mode::Rectangle:
+    case ClipMode::Rectangle:
         rectangleModeClipRectWithTransform(r, transform, op);
         break;
-    case Mode::RectangleList:
+    case ClipMode::RectangleList:
         rectangleListModeClipRectWithTransform(r, transform, op);
         break;
-    case Mode::Region:
+    case ClipMode::Region:
         regionModeClipRectWithTransform(r, transform, op);
         break;
     }
 }
 
 void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
+    onClipUpdated();
     enterRegionMode();
     mClipRegion.op(region, op);
     onClipRegionUpdated();
@@ -224,6 +236,7 @@
 
 void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
         SkRegion::Op op) {
+    onClipUpdated();
     SkMatrix skTransform;
     transform->copyTo(skTransform);
     SkPath transformed;
@@ -241,7 +254,7 @@
     // Entering rectangle mode discards any
     // existing clipping information from the other modes.
     // The only way this occurs is by a clip setting operation.
-    mMode = Mode::Rectangle;
+    mMode = ClipMode::Rectangle;
 }
 
 void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
@@ -276,8 +289,8 @@
     // Is is only legal to enter rectangle list mode from
     // rectangle mode, since rectangle list mode cannot represent
     // all clip areas that can be represented by a region.
-    ALOG_ASSERT(mMode == Mode::Rectangle);
-    mMode = Mode::RectangleList;
+    ALOG_ASSERT(mMode == ClipMode::Rectangle);
+    mMode = ClipMode::RectangleList;
     mRectangleList.set(mClipRect, Matrix4::identity());
 }
 
@@ -295,12 +308,11 @@
  */
 
 void ClipArea::enterRegionMode() {
-    Mode oldMode = mMode;
-    mMode = Mode::Region;
-    if (oldMode != Mode::Region) {
-        if (oldMode == Mode::Rectangle) {
-            mClipRegion.setRect(mClipRect.left, mClipRect.top,
-                    mClipRect.right, mClipRect.bottom);
+    ClipMode oldMode = mMode;
+    mMode = ClipMode::Region;
+    if (oldMode != ClipMode::Region) {
+        if (oldMode == ClipMode::Rectangle) {
+            mClipRegion.setRect(mClipRect.toSkIRect());
         } else {
             mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
             onClipRegionUpdated();
@@ -330,5 +342,172 @@
     }
 }
 
+/**
+ * Clip serialization
+ */
+
+const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
+    if (!mPostViewportClipObserved) {
+        // Only initial clip-to-viewport observed, so no serialization of clip necessary
+        return nullptr;
+    }
+
+    static_assert(std::is_trivially_destructible<Rect>::value,
+            "expect Rect to be trivially destructible");
+    static_assert(std::is_trivially_destructible<RectangleList>::value,
+            "expect RectangleList to be trivially destructible");
+
+    if (mLastSerialization == nullptr) {
+        switch (mMode) {
+        case ClipMode::Rectangle:
+            mLastSerialization = allocator.create<ClipRect>(mClipRect);
+            break;
+        case ClipMode::RectangleList:
+            mLastSerialization = allocator.create<ClipRectList>(mRectangleList);
+            break;
+        case ClipMode::Region:
+            mLastSerialization = allocator.create<ClipRegion>(mClipRegion);
+            break;
+        }
+    }
+    return mLastSerialization;
+}
+
+inline static const Rect& getRect(const ClipBase* scb) {
+    return reinterpret_cast<const ClipRect*>(scb)->rect;
+}
+
+inline static const RectangleList& getRectList(const ClipBase* scb) {
+    return reinterpret_cast<const ClipRectList*>(scb)->rectList;
+}
+
+inline static const SkRegion& getRegion(const ClipBase* scb) {
+    return reinterpret_cast<const ClipRegion*>(scb)->region;
+}
+
+// Conservative check for too many rectangles to fit in rectangle list.
+// For simplicity, doesn't account for rect merging
+static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
+    int currentRectCount = clipArea.isRectangleList()
+            ? clipArea.getRectangleList().getTransformedRectanglesCount()
+            : 1;
+    int recordedRectCount = (scb->mode == ClipMode::RectangleList)
+            ? getRectList(scb).getTransformedRectanglesCount()
+            : 1;
+    return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
+}
+
+const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
+        const ClipBase* recordedClip, const Matrix4& recordedClipTransform) {
+    // if no recordedClip passed, just serialize current state
+    if (!recordedClip) return serializeClip(allocator);
+
+    if (!mLastResolutionResult
+            || recordedClip != mLastResolutionClip
+            || recordedClipTransform != mLastResolutionTransform) {
+        mLastResolutionClip = recordedClip;
+        mLastResolutionTransform = recordedClipTransform;
+
+        if (CC_LIKELY(mMode == ClipMode::Rectangle
+                && recordedClip->mode == ClipMode::Rectangle
+                && recordedClipTransform.rectToRect())) {
+            // common case - result is a single rectangle
+            auto rectClip = allocator.create<ClipRect>(getRect(recordedClip));
+            recordedClipTransform.mapRect(rectClip->rect);
+            rectClip->rect.doIntersect(mClipRect);
+            mLastResolutionResult = rectClip;
+        } else if (CC_UNLIKELY(mMode == ClipMode::Region
+                || recordedClip->mode == ClipMode::Region
+                || cannotFitInRectangleList(*this, recordedClip))) {
+            // region case
+            SkRegion other;
+            switch (recordedClip->mode) {
+            case ClipMode::Rectangle:
+                if (CC_LIKELY(recordedClipTransform.rectToRect())) {
+                    // simple transform, skip creating SkPath
+                    Rect resultClip(getRect(recordedClip));
+                    recordedClipTransform.mapRect(resultClip);
+                    other.setRect(resultClip.toSkIRect());
+                } else {
+                    SkPath transformedRect = pathFromTransformedRectangle(getRect(recordedClip),
+                            recordedClipTransform);
+                    other.setPath(transformedRect, createViewportRegion());
+                }
+                break;
+            case ClipMode::RectangleList: {
+                RectangleList transformedList(getRectList(recordedClip));
+                transformedList.transform(recordedClipTransform);
+                other = transformedList.convertToRegion(createViewportRegion());
+                break;
+            }
+            case ClipMode::Region:
+                other = getRegion(recordedClip);
+
+                // TODO: handle non-translate transforms properly!
+                other.translate(recordedClipTransform.getTranslateX(),
+                        recordedClipTransform.getTranslateY());
+            }
+
+            ClipRegion* regionClip = allocator.create<ClipRegion>();
+            switch (mMode) {
+            case ClipMode::Rectangle:
+                regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
+                break;
+            case ClipMode::RectangleList:
+                regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
+                        other, SkRegion::kIntersect_Op);
+                break;
+            case ClipMode::Region:
+                regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
+                break;
+            }
+            regionClip->rect.set(regionClip->region.getBounds());
+            mLastResolutionResult = regionClip;
+        } else {
+            auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
+            auto&& rectList = rectListClip->rectList;
+            if (mMode == ClipMode::Rectangle) {
+                rectList.set(mClipRect, Matrix4::identity());
+            }
+
+            if (recordedClip->mode == ClipMode::Rectangle) {
+                rectList.intersectWith(getRect(recordedClip), recordedClipTransform);
+            } else {
+                const RectangleList& other = getRectList(recordedClip);
+                for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
+                    auto&& tr = other.getTransformedRectangle(i);
+                    Matrix4 totalTransform(recordedClipTransform);
+                    totalTransform.multiply(tr.getTransform());
+                    rectList.intersectWith(tr.getBounds(), totalTransform);
+                }
+            }
+            rectListClip->rect = rectList.calculateBounds();
+            mLastResolutionResult = rectListClip;
+        }
+    }
+    return mLastResolutionResult;
+}
+
+void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
+    if (!clip) return; // nothing to do
+
+    if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
+        clipRectWithTransform(getRect(clip), &transform, SkRegion::kIntersect_Op);
+    } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
+        auto&& rectList = getRectList(clip);
+        for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
+            auto&& tr = rectList.getTransformedRectangle(i);
+            Matrix4 totalTransform(transform);
+            totalTransform.multiply(tr.getTransform());
+            clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
+        }
+    } else {
+        SkRegion region(getRegion(clip));
+        // TODO: handle non-translate transforms properly!
+        region.translate(transform.getTranslateX(), transform.getTranslateY());
+        clipRegion(region, SkRegion::kIntersect_Op);
+    }
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 268301c..479796d 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -16,15 +16,17 @@
 #ifndef CLIPAREA_H
 #define CLIPAREA_H
 
-#include <SkRegion.h>
-
 #include "Matrix.h"
 #include "Rect.h"
 #include "utils/Pair.h"
 
+#include <SkRegion.h>
+
 namespace android {
 namespace uirenderer {
 
+class LinearAllocator;
+
 Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
 
 class TransformedRectangle {
@@ -50,6 +52,12 @@
         return mTransform;
     }
 
+    void transform(const Matrix4& transform) {
+        Matrix4 t;
+        t.loadMultiply(transform, mTransform);
+        mTransform = t;
+    }
+
 private:
     Rect mBounds;
     Matrix4 mTransform;
@@ -66,27 +74,62 @@
     void setEmpty();
     void set(const Rect& bounds, const Matrix4& transform);
     bool intersectWith(const Rect& bounds, const Matrix4& transform);
+    void transform(const Matrix4& transform);
 
     SkRegion convertToRegion(const SkRegion& clip) const;
     Rect calculateBounds() const;
 
-private:
     enum {
         kMaxTransformedRectangles = 5
     };
 
+private:
     int mTransformedRectanglesCount;
     TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
 };
 
-class ClipArea {
-private:
-    enum class Mode {
-        Rectangle,
-        Region,
-        RectangleList
-    };
+enum class ClipMode {
+    Rectangle,
+    RectangleList,
 
+    // region and path - intersected. if either is empty, don't use
+    Region
+};
+
+struct ClipBase {
+    ClipBase(ClipMode mode)
+            : mode(mode) {}
+    ClipBase(const Rect& rect)
+            : mode(ClipMode::Rectangle)
+            , rect(rect) {}
+    const ClipMode mode;
+    // Bounds of the clipping area, used to define the scissor, and define which
+    // portion of the stencil is updated/used
+    Rect rect;
+};
+
+struct ClipRect : ClipBase {
+    ClipRect(const Rect& rect)
+            : ClipBase(rect) {}
+};
+
+struct ClipRectList : ClipBase {
+    ClipRectList(const RectangleList& rectList)
+            : ClipBase(ClipMode::RectangleList)
+            , rectList(rectList) {}
+    RectangleList rectList;
+};
+
+struct ClipRegion : ClipBase {
+    ClipRegion(const SkRegion& region)
+            : ClipBase(ClipMode::Region)
+            , region(region) {}
+    ClipRegion()
+            : ClipBase(ClipMode::Region) {}
+    SkRegion region;
+};
+
+class ClipArea {
 public:
     ClipArea();
 
@@ -117,17 +160,22 @@
     }
 
     bool isRegion() const {
-        return Mode::Region == mMode;
+        return ClipMode::Region == mMode;
     }
 
     bool isSimple() const {
-        return mMode == Mode::Rectangle;
+        return mMode == ClipMode::Rectangle;
     }
 
     bool isRectangleList() const {
-        return mMode == Mode::RectangleList;
+        return mMode == ClipMode::RectangleList;
     }
 
+    const ClipBase* serializeClip(LinearAllocator& allocator);
+    const ClipBase* serializeIntersectedClip(LinearAllocator& allocator,
+            const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
+    void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
+
 private:
     void enterRectangleMode();
     void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
@@ -145,6 +193,13 @@
     void ensureClipRegion();
     void onClipRegionUpdated();
 
+    // Called by every state modifying public method.
+    void onClipUpdated() {
+        mPostViewportClipObserved = true;
+        mLastSerialization = nullptr;
+        mLastResolutionResult = nullptr;
+    }
+
     SkRegion createViewportRegion() {
         return SkRegion(mViewportBounds.toSkIRect());
     }
@@ -155,7 +210,22 @@
         pathAsRegion.setPath(path, createViewportRegion());
     }
 
-    Mode mMode;
+    ClipMode mMode;
+    bool mPostViewportClipObserved = false;
+
+    /**
+     * If mLastSerialization is non-null, it represents an already serialized copy
+     * of the current clip state. If null, it has not been computed.
+     */
+    const ClipBase* mLastSerialization = nullptr;
+
+    /**
+     * This pair of pointers is a single entry cache of most recently seen
+     */
+    const ClipBase* mLastResolutionResult = nullptr;
+    const ClipBase* mLastResolutionClip = nullptr;
+    Matrix4 mLastResolutionTransform;
+
     Rect mViewportBounds;
     Rect mClipRect;
     SkRegion mClipRegion;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index ff4dc4a..9994498 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -47,6 +47,7 @@
 #if HWUI_NEW_OPS
 class BakedOpState;
 class BakedOpRenderer;
+struct ClipBase;
 #else
 class OpenGLRenderer;
 #endif
@@ -57,7 +58,7 @@
 #if HWUI_NEW_OPS
             BakedOpRenderer* renderer,
             const BakedOpState* bakedState,
-            const Rect* clip,
+            const ClipBase* clip,
 #else
             OpenGLRenderer* renderer,
 #endif
@@ -81,7 +82,7 @@
 #if HWUI_NEW_OPS
     BakedOpRenderer* renderer;
     const BakedOpState* bakedState;
-    const Rect* clip;
+    const ClipBase* clip;
 #else
     OpenGLRenderer* renderer;
 #endif
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/FrameBuilder.cpp
similarity index 60%
rename from libs/hwui/OpReorderer.cpp
rename to libs/hwui/FrameBuilder.cpp
index ad9559f..c4c655b 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "OpReorderer.h"
+#include "FrameBuilder.h"
 
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
@@ -30,331 +30,46 @@
 namespace android {
 namespace uirenderer {
 
-class BatchBase {
-
-public:
-    BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
-            : mBatchId(batchId)
-            , mMerging(merging) {
-        mBounds = op->computedState.clippedBounds;
-        mOps.push_back(op);
-    }
-
-    bool intersects(const Rect& rect) const {
-        if (!rect.intersects(mBounds)) return false;
-
-        for (const BakedOpState* op : mOps) {
-            if (rect.intersects(op->computedState.clippedBounds)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    batchid_t getBatchId() const { return mBatchId; }
-    bool isMerging() const { return mMerging; }
-
-    const std::vector<BakedOpState*>& getOps() const { return mOps; }
-
-    void dump() const {
-        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
-                this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
-    }
-protected:
-    batchid_t mBatchId;
-    Rect mBounds;
-    std::vector<BakedOpState*> mOps;
-    bool mMerging;
-};
-
-class OpBatch : public BatchBase {
-public:
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
-    OpBatch(batchid_t batchId, BakedOpState* op)
-            : BatchBase(batchId, op, false) {
-    }
-
-    void batchOp(BakedOpState* op) {
-        mBounds.unionWith(op->computedState.clippedBounds);
-        mOps.push_back(op);
-    }
-};
-
-class MergingOpBatch : public BatchBase {
-public:
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc(size);
-    }
-
-    MergingOpBatch(batchid_t batchId, BakedOpState* op)
-            : BatchBase(batchId, op, true)
-            , mClipSideFlags(op->computedState.clipSideFlags) {
-    }
-
-    /*
-     * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
-     * and clip side flags. Positive bounds delta means new bounds fit in old.
-     */
-    static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
-            float boundsDelta) {
-        bool currentClipExists = currentFlags & side;
-        bool newClipExists = newFlags & side;
-
-        // if current is clipped, we must be able to fit new bounds in current
-        if (boundsDelta > 0 && currentClipExists) return false;
-
-        // if new is clipped, we must be able to fit current bounds in new
-        if (boundsDelta < 0 && newClipExists) return false;
-
-        return true;
-    }
-
-    static bool paintIsDefault(const SkPaint& paint) {
-        return paint.getAlpha() == 255
-                && paint.getColorFilter() == nullptr
-                && paint.getShader() == nullptr;
-    }
-
-    static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
-        return a.getAlpha() == b.getAlpha()
-                && a.getColorFilter() == b.getColorFilter()
-                && a.getShader() == b.getShader();
-    }
-
-    /*
-     * Checks if a (mergeable) op can be merged into this batch
-     *
-     * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
-     * important to consider all paint attributes used in the draw calls in deciding both a) if an
-     * op tries to merge at all, and b) if the op can merge with another set of ops
-     *
-     * False positives can lead to information from the paints of subsequent merged operations being
-     * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
-     */
-    bool canMergeWith(BakedOpState* op) const {
-        bool isTextBatch = getBatchId() == OpBatchType::Text
-                || getBatchId() == OpBatchType::ColorText;
-
-        // Overlapping other operations is only allowed for text without shadow. For other ops,
-        // multiDraw isn't guaranteed to overdraw correctly
-        if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
-            if (intersects(op->computedState.clippedBounds)) return false;
-        }
-
-        const BakedOpState* lhs = op;
-        const BakedOpState* rhs = mOps[0];
-
-        if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
-
-        // Identical round rect clip state means both ops will clip in the same way, or not at all.
-        // As the state objects are const, we can compare their pointers to determine mergeability
-        if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
-        if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
-
-        /* Clipping compatibility check
-         *
-         * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
-         * clip for that side.
-         */
-        const int currentFlags = mClipSideFlags;
-        const int newFlags = op->computedState.clipSideFlags;
-        if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
-            const Rect& opBounds = op->computedState.clippedBounds;
-            float boundsDelta = mBounds.left - opBounds.left;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
-            boundsDelta = mBounds.top - opBounds.top;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
-
-            // right and bottom delta calculation reversed to account for direction
-            boundsDelta = opBounds.right - mBounds.right;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
-            boundsDelta = opBounds.bottom - mBounds.bottom;
-            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
-        }
-
-        const SkPaint* newPaint = op->op->paint;
-        const SkPaint* oldPaint = mOps[0]->op->paint;
-
-        if (newPaint == oldPaint) {
-            // if paints are equal, then modifiers + paint attribs don't need to be compared
-            return true;
-        } else if (newPaint && !oldPaint) {
-            return paintIsDefault(*newPaint);
-        } else if (!newPaint && oldPaint) {
-            return paintIsDefault(*oldPaint);
-        }
-        return paintsAreEquivalent(*newPaint, *oldPaint);
-    }
-
-    void mergeOp(BakedOpState* op) {
-        mBounds.unionWith(op->computedState.clippedBounds);
-        mOps.push_back(op);
-
-        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
-        // check, and doesn't extend past a side of the clip that's in use by the merged batch.
-        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
-        mClipSideFlags |= op->computedState.clipSideFlags;
-    }
-
-    int getClipSideFlags() const { return mClipSideFlags; }
-    const Rect& getClipRect() const { return mBounds; }
-
-private:
-    int mClipSideFlags;
-};
-
-OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
-        const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
-        : width(width)
-        , height(height)
-        , repaintRect(repaintRect)
-        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
-        , beginLayerOp(beginLayerOp)
-        , renderNode(renderNode) {}
-
-// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still iterate to find similar batch to insert after
-void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
-        BatchBase** targetBatch, size_t* insertBatchIndex) const {
-    for (int i = mBatches.size() - 1; i >= 0; i--) {
-        BatchBase* overBatch = mBatches[i];
-
-        if (overBatch == *targetBatch) break;
-
-        // TODO: also consider shader shared between batch types
-        if (batchId == overBatch->getBatchId()) {
-            *insertBatchIndex = i + 1;
-            if (!*targetBatch) break; // found insert position, quit
-        }
-
-        if (overBatch->intersects(clippedBounds)) {
-            // NOTE: it may be possible to optimize for special cases where two operations
-            // of the same batch/paint could swap order, such as with a non-mergeable
-            // (clipped) and a mergeable text operation
-            *targetBatch = nullptr;
-            break;
-        }
-    }
-}
-
-void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
-        BakedOpState* op, batchid_t batchId) {
-    OpBatch* targetBatch = mBatchLookup[batchId];
-
-    size_t insertBatchIndex = mBatches.size();
-    if (targetBatch) {
-        locateInsertIndex(batchId, op->computedState.clippedBounds,
-                (BatchBase**)(&targetBatch), &insertBatchIndex);
-    }
-
-    if (targetBatch) {
-        targetBatch->batchOp(op);
-    } else  {
-        // new non-merging batch
-        targetBatch = new (allocator) OpBatch(batchId, op);
-        mBatchLookup[batchId] = targetBatch;
-        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
-    }
-}
-
-// insertion point of a new batch, will hopefully be immediately after similar batch
-// (generally, should be similar shader)
-void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
-        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
-    MergingOpBatch* targetBatch = nullptr;
-
-    // Try to merge with any existing batch with same mergeId
-    auto getResult = mMergingBatchLookup[batchId].find(mergeId);
-    if (getResult != mMergingBatchLookup[batchId].end()) {
-        targetBatch = getResult->second;
-        if (!targetBatch->canMergeWith(op)) {
-            targetBatch = nullptr;
-        }
-    }
-
-    size_t insertBatchIndex = mBatches.size();
-    locateInsertIndex(batchId, op->computedState.clippedBounds,
-            (BatchBase**)(&targetBatch), &insertBatchIndex);
-
-    if (targetBatch) {
-        targetBatch->mergeOp(op);
-    } else  {
-        // new merging batch
-        targetBatch = new (allocator) MergingOpBatch(batchId, op);
-        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
-
-        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
-    }
-}
-
-void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg,
-        BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
-    ATRACE_NAME("flush drawing commands");
-    for (const BatchBase* batch : mBatches) {
-        size_t size = batch->getOps().size();
-        if (size > 1 && batch->isMerging()) {
-            int opId = batch->getOps()[0]->op->opId;
-            const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
-            MergedBakedOpList data = {
-                    batch->getOps().data(),
-                    size,
-                    mergingBatch->getClipSideFlags(),
-                    mergingBatch->getClipRect()
-            };
-            mergedReceivers[opId](arg, data);
-        } else {
-            for (const BakedOpState* op : batch->getOps()) {
-                unmergedReceivers[op->op->opId](arg, *op);
-            }
-        }
-    }
-}
-
-void OpReorderer::LayerReorderer::dump() const {
-    ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
-            this, width, height, offscreenBuffer, beginLayerOp, renderNode);
-    for (const BatchBase* batch : mBatches) {
-        batch->dump();
-    }
-}
-
-OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
         uint32_t viewportWidth, uint32_t viewportHeight,
         const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
         : mCanvasState(*this) {
     ATRACE_NAME("prepare drawing commands");
 
-    mLayerReorderers.reserve(layers.entries().size());
+    mLayerBuilders.reserve(layers.entries().size());
     mLayerStack.reserve(layers.entries().size());
 
     // Prepare to defer Fbo0
-    mLayerReorderers.emplace_back(viewportWidth, viewportHeight, Rect(clip));
+    auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
+    mLayerBuilders.push_back(fbo0);
     mLayerStack.push_back(0);
     mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
             clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
             lightCenter);
 
     // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
-    // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+    // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
     for (int i = layers.entries().size() - 1; i >= 0; i--) {
         RenderNode* layerNode = layers.entries()[i].renderNode;
-        const Rect& layerDamage = layers.entries()[i].damage;
-        layerNode->computeOrdering();
+        // only schedule repaint if node still on layer - possible it may have been
+        // removed during a dropped frame, but layers may still remain scheduled so
+        // as not to lose info on what portion is damaged
+        if (CC_LIKELY(layerNode->getLayer() != nullptr)) {
+            const Rect& layerDamage = layers.entries()[i].damage;
+            layerNode->computeOrdering();
 
-        // map current light center into RenderNode's coordinate space
-        Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
-        layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
+            // map current light center into RenderNode's coordinate space
+            Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
+            layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
 
-        saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
-                layerDamage, lightCenter, nullptr, layerNode);
+            saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
+                    layerDamage, lightCenter, nullptr, layerNode);
 
-        if (layerNode->getDisplayList()) {
-            deferNodeOps(*layerNode);
+            if (layerNode->getDisplayList()) {
+                deferNodeOps(*layerNode);
+            }
+            restoreForLayer();
         }
-        restoreForLayer();
     }
 
     // Defer Fbo0
@@ -368,11 +83,11 @@
     }
 }
 
-void OpReorderer::onViewportInitialized() {}
+void FrameBuilder::onViewportInitialized() {}
 
-void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
 
-void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
+void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
     const RenderProperties& properties = node.properties();
     const Outline& outline = properties.getOutline();
     if (properties.getAlpha() <= 0
@@ -460,7 +175,7 @@
             deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
                     saveLayerBounds,
                     Matrix4::identity(),
-                    saveLayerBounds,
+                    nullptr, // no record-time clip - need only respect defer-time one
                     &saveLayerPaint));
             deferNodeOps(node);
             deferEndLayerOp(*new (mAllocator) EndLayerOp());
@@ -504,7 +219,7 @@
 }
 
 template <typename V>
-void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameBuilder::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
     const int size = zTranslatedNodes.size();
     if (size == 0
             || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -554,7 +269,7 @@
     }
 }
 
-void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
     auto& node = *casterNodeOp.renderNode;
     auto& properties = node.properties();
 
@@ -604,13 +319,13 @@
             mCanvasState.getLocalClipBounds(),
             mCanvasState.currentSnapshot()->getRelativeLightCenter());
     BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct(
-            mAllocator, *mCanvasState.currentSnapshot(), shadowOp);
+            mAllocator, *mCanvasState.writableSnapshot(), shadowOp);
     if (CC_LIKELY(bakedOpState)) {
         currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
     }
 }
 
-void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {
+void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
     const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
     int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
 
@@ -643,18 +358,16 @@
 }
 
 /**
- * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods.
+ * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods.
  *
  * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
+ * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&)
  */
 #define OP_RECEIVER(Type) \
-        [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
-void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
-    typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op);
-    static OpDispatcher receivers[] = {
-        MAP_OPS(OP_RECEIVER)
-    };
+        [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); },
+void FrameBuilder::deferNodeOps(const RenderNode& renderNode) {
+    typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op);
+    static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
 
     // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
     const DisplayList& displayList = *(renderNode.getDisplayList());
@@ -677,14 +390,14 @@
     }
 }
 
-void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) return;
     int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
 
-    // apply state from RecordedOp
+    // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix)
+    mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip,
+            *mCanvasState.currentSnapshot()->transform);
     mCanvasState.concatMatrix(op.localMatrix);
-    mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
-            op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
 
     // then apply state from node properties, and defer ops
     deferNodePropsAndOps(*op.renderNode);
@@ -692,7 +405,7 @@
     mCanvasState.restoreToCount(count);
 }
 
-void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
     if (!op.skipInOrderDraw) {
         deferRenderNodeOpImpl(op);
     }
@@ -702,11 +415,11 @@
  * Defers an unmergeable, strokeable op, accounting correctly
  * for paint's style on the bounds being computed.
  */
-void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
         BakedOpState::StrokeBehavior strokeBehavior) {
     // Note: here we account for stroke when baking the op
     BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
-            mAllocator, *mCanvasState.currentSnapshot(), op, strokeBehavior);
+            mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
 }
@@ -724,11 +437,16 @@
             : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
 }
 
-void OpReorderer::deferArcOp(const ArcOp& op) {
+void FrameBuilder::deferArcOp(const ArcOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferBitmapOp(const BitmapOp& op) {
+static bool hasMergeableClip(const BakedOpState& state) {
+    return state.computedState.clipState
+            || state.computedState.clipState->mode == ClipMode::Rectangle;
+}
+
+void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -738,8 +456,9 @@
     if (bakedState->computedState.transform.isSimple()
             && bakedState->computedState.transform.positiveScale()
             && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
-            && op.bitmap->colorType() != kAlpha_8_SkColorType) {
-        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+            && op.bitmap->colorType() != kAlpha_8_SkColorType
+            && hasMergeableClip(*bakedState)) {
+        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
         // TODO: AssetAtlas in mergeId
         currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
     } else {
@@ -747,19 +466,19 @@
     }
 }
 
-void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
+void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
+void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
     // allocate a temporary oval op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
     float x = *(op.x);
@@ -769,33 +488,34 @@
     const OvalOp* resolvedOp = new (mAllocator) OvalOp(
             unmappedBounds,
             op.localMatrix,
-            op.localClipRect,
+            op.localClip,
             op.paint);
     deferOvalOp(*resolvedOp);
 }
 
-void OpReorderer::deferFunctorOp(const FunctorOp& op) {
+void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
 }
 
-void OpReorderer::deferLinesOp(const LinesOp& op) {
+void FrameBuilder::deferLinesOp(const LinesOp& op) {
     batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
     deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
 }
 
-void OpReorderer::deferOvalOp(const OvalOp& op) {
+void FrameBuilder::deferOvalOp(const OvalOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferPatchOp(const PatchOp& op) {
+void FrameBuilder::deferPatchOp(const PatchOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
     if (bakedState->computedState.transform.isPureTranslate()
-            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
-        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+            && hasMergeableClip(*bakedState)) {
+        mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
         // TODO: AssetAtlas in mergeId
 
         // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
@@ -806,35 +526,35 @@
     }
 }
 
-void OpReorderer::deferPathOp(const PathOp& op) {
+void FrameBuilder::deferPathOp(const PathOp& op) {
     deferStrokeableOp(op, OpBatchType::Bitmap);
 }
 
-void OpReorderer::deferPointsOp(const PointsOp& op) {
+void FrameBuilder::deferPointsOp(const PointsOp& op) {
     batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
     deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
 }
 
-void OpReorderer::deferRectOp(const RectOp& op) {
+void FrameBuilder::deferRectOp(const RectOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferRoundRectOp(const RoundRectOp& op) {
+void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
     deferStrokeableOp(op, tessBatchId(op));
 }
 
-void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
     // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
     const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
             Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
             op.localMatrix,
-            op.localClipRect,
+            op.localClip,
             op.paint, *op.rx, *op.ry);
     deferRoundRectOp(*resolvedOp);
 }
 
-void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
+void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
@@ -845,13 +565,14 @@
     return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
 }
 
-void OpReorderer::deferTextOp(const TextOp& op) {
+void FrameBuilder::deferTextOp(const TextOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
     batchid_t batchId = textBatchId(*(op.paint));
     if (bakedState->computedState.transform.isPureTranslate()
-            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
+            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+            && hasMergeableClip(*bakedState)) {
         mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
         currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
     } else {
@@ -859,19 +580,19 @@
     }
 }
 
-void OpReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
+void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
 }
 
-void OpReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
+void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
 }
 
-void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
         float contentTranslateX, float contentTranslateY,
         const Rect& repaintRect,
         const Vector3& lightCenter,
@@ -886,18 +607,21 @@
             repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom);
 
     // create a new layer repaint, and push its index on the stack
-    mLayerStack.push_back(mLayerReorderers.size());
-    mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
+    mLayerStack.push_back(mLayerBuilders.size());
+    auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight,
+            repaintRect, beginLayerOp, renderNode);
+    mLayerBuilders.push_back(newFbo);
 }
 
-void OpReorderer::restoreForLayer() {
+void FrameBuilder::restoreForLayer() {
     // restore canvas, and pop finished layer off of the stack
     mCanvasState.restore();
     mLayerStack.pop_back();
 }
 
-// TODO: test rejection at defer time, where the bounds become empty
-void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
+// TODO: defer time rejection (when bounds become empty) + tests
+// Option - just skip layers with no bounds at playback + defer?
+void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) {
     uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
     uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
 
@@ -906,7 +630,7 @@
 
     // Combine all transforms used to present saveLayer content:
     // parent content transform * canvas transform * bounds offset
-    Matrix4 contentTransform(*previous->transform);
+    Matrix4 contentTransform(*(previous->transform));
     contentTransform.multiply(op.localMatrix);
     contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top);
 
@@ -942,7 +666,7 @@
             &op, nullptr);
 }
 
-void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
+void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
     const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
     int finishedLayerIndex = mLayerStack.back();
 
@@ -953,9 +677,9 @@
     LayerOp* drawLayerOp = new (mAllocator) LayerOp(
             beginLayerOp.unmappedBounds,
             beginLayerOp.localMatrix,
-            beginLayerOp.localClipRect,
+            beginLayerOp.localClip,
             beginLayerOp.paint,
-            &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
+            &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
     BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
 
     if (bakedOpState) {
@@ -963,17 +687,54 @@
         currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
     } else {
         // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
-        mLayerReorderers[finishedLayerIndex].clear();
+        // TODO: need to prevent any render work from being done
+        // - create layerop earlier for reject purposes?
+        mLayerBuilders[finishedLayerIndex]->clear();
         return;
     }
 }
 
-void OpReorderer::deferLayerOp(const LayerOp& op) {
-    LOG_ALWAYS_FATAL("unsupported");
+void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
+    Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
+    boundsTransform.multiply(op.localMatrix);
+
+    Rect dstRect(op.unmappedBounds);
+    boundsTransform.mapRect(dstRect);
+    dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
+
+    // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume)
+    OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
+
+    /**
+     * First, defer an operation to copy out the content from the rendertarget into a layer.
+     */
+    auto copyToOp = new (mAllocator) CopyToLayerOp(op, layerHandle);
+    BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator,
+            &(currentLayer().viewportClip), dstRect, *copyToOp);
+    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
+
+    /**
+     * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
+     * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
+     */
+    currentLayer().deferLayerClear(dstRect);
+
+    /**
+     * And stash an operation to copy that layer back under the rendertarget until
+     * a balanced EndUnclippedLayerOp is seen
+     */
+    auto copyFromOp = new (mAllocator) CopyFromLayerOp(op, layerHandle);
+    bakedState = BakedOpState::directConstruct(mAllocator,
+            &(currentLayer().viewportClip), dstRect, *copyFromOp);
+    currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
 }
 
-void OpReorderer::deferShadowOp(const ShadowOp& op) {
-    LOG_ALWAYS_FATAL("unsupported");
+void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
+    LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
+
+    BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
+    currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
+    currentLayer().activeUnclippedSaveLayers.pop_back();
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
new file mode 100644
index 0000000..3ba73f0
--- /dev/null
+++ b/libs/hwui/FrameBuilder.h
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "BakedOpState.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "LayerBuilder.h"
+#include "RecordedOp.h"
+
+#include <vector>
+#include <unordered_map>
+
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class BakedOpState;
+class LayerUpdateQueue;
+class OffscreenBuffer;
+class Rect;
+
+/**
+ * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
+ * them to be rendered.
+ *
+ * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
+ * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
+ * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
+ * draw stream) will create different reorder contexts, each in its own LayerBuilder.
+ *
+ * Then the prepared or 'baked' drawing commands can be issued by calling the templated
+ * replayBakedOps() function, which will dispatch them (including any created merged op collections)
+ * to a Dispatcher and Renderer. See BakedOpDispatcher for how these baked drawing operations are
+ * resolved into Glops and rendered via BakedOpRenderer.
+ *
+ * This class is also the authoritative source for traversing RenderNodes, both for standard op
+ * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
+ */
+class FrameBuilder : public CanvasStateClient {
+public:
+    FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
+            uint32_t viewportWidth, uint32_t viewportHeight,
+            const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
+
+    virtual ~FrameBuilder() {}
+
+    /**
+     * replayBakedOps() is templated based on what class will receive ops being replayed.
+     *
+     * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
+     * state->op->opId to lookup a receiver that will be called when the op is replayed.
+     *
+     */
+    template <typename StaticDispatcher, typename Renderer>
+    void replayBakedOps(Renderer& renderer) {
+        /**
+         * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to
+         * dispatch the op via a method on a static dispatcher when the op is replayed.
+         *
+         * For example a BitmapOp would resolve, via the lambda lookup, to calling:
+         *
+         * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state);
+         */
+        #define X(Type) \
+                [](void* renderer, const BakedOpState& state) { \
+                    StaticDispatcher::on##Type(*(static_cast<Renderer*>(renderer)), \
+                            static_cast<const Type&>(*(state.op)), state); \
+                },
+        static BakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
+        #undef X
+
+        /**
+         * Defines a LUT of lambdas which allow merged arrays of BakedOpState* to be passed to a
+         * static dispatcher when the group of merged ops is replayed.
+         */
+        #define X(Type) \
+                [](void* renderer, const MergedBakedOpList& opList) { \
+                    StaticDispatcher::onMerged##Type##s(*(static_cast<Renderer*>(renderer)), opList); \
+                },
+        static MergedOpReceiver mergedReceivers[] = BUILD_MERGEABLE_OP_LUT(X);
+        #undef X
+
+        // Relay through layers in reverse order, since layers
+        // later in the list will be drawn by earlier ones
+        for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
+            LayerBuilder& layer = *(mLayerBuilders[i]);
+            if (layer.renderNode) {
+                // cached HW layer - can't skip layer if empty
+                renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
+                layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
+                renderer.endLayer();
+            } else if (!layer.empty()) { // save layer - skip entire layer if empty
+                layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
+                layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
+                renderer.endLayer();
+            }
+        }
+
+        const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
+        renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
+        fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
+        renderer.endFrame(fbo0.repaintRect);
+    }
+
+    void dump() const {
+        for (auto&& layer : mLayerBuilders) {
+            layer->dump();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    /// CanvasStateClient interface
+    ///////////////////////////////////////////////////////////////////
+    virtual void onViewportInitialized() override;
+    virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
+    virtual GLuint getTargetFbo() const override { return 0; }
+
+private:
+    enum class ChildrenSelectMode {
+        Negative,
+        Positive
+    };
+    void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+            float contentTranslateX, float contentTranslateY,
+            const Rect& repaintRect,
+            const Vector3& lightCenter,
+            const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+    void restoreForLayer();
+
+    LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); }
+
+    BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
+        return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
+    }
+
+    // should always be surrounded by a save/restore pair, and not called if DisplayList is null
+    void deferNodePropsAndOps(RenderNode& node);
+
+    template <typename V>
+    void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
+
+    void deferShadow(const RenderNodeOp& casterOp);
+
+    void deferProjectedChildren(const RenderNode& renderNode);
+
+    void deferNodeOps(const RenderNode& renderNode);
+
+    void deferRenderNodeOpImpl(const RenderNodeOp& op);
+
+    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
+
+    SkPath* createFrameAllocatedPath() {
+        return mAllocator.create<SkPath>();
+    }
+
+    void deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+            BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
+
+    /**
+     * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type.
+     *
+     * These private methods are called from within deferImpl to defer each individual op
+     * type differently.
+     */
+#define X(Type) void defer##Type(const Type& op);
+    MAP_DEFERRABLE_OPS(X)
+#undef X
+
+    // List of every deferred layer's render state. Replayed in reverse order to render a frame.
+    std::vector<LayerBuilder*> mLayerBuilders;
+
+    /*
+     * Stack of indices within mLayerBuilders representing currently active layers. If drawing
+     * layerA within a layerB, will contain, in order:
+     *  - 0 (representing FBO 0, always present)
+     *  - layerB's index
+     *  - layerA's index
+     *
+     * Note that this doesn't vector doesn't always map onto all values of mLayerBuilders. When a
+     * layer is finished deferring, it will still be represented in mLayerBuilders, but it's index
+     * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
+     * ops added to it.
+    */
+    std::vector<size_t> mLayerStack;
+
+    CanvasState mCanvasState;
+
+    // contains ResolvedOps and Batches
+    LinearAllocator mAllocator;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index bcf819e..e72f396 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -64,7 +64,7 @@
 
         // Canvas transform isn't applied to the mesh at draw time,
         //since it's already built in.
-        MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove
+        MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS
     };
 };
 
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 2507ff3..45fc16c 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -78,7 +78,7 @@
     mOutGlop->mesh.vertices = {
             vbo,
             VertexAttribFlags::TextureCoord,
-            nullptr, nullptr, nullptr,
+            nullptr, (const void*) kMeshTextureOffset, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = elementCount;
     return *this;
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
new file mode 100644
index 0000000..4fb5701
--- /dev/null
+++ b/libs/hwui/GpuMemoryTracker.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 "utils/StringUtils.h"
+#include "Texture.h"
+
+#include <cutils/compiler.h>
+#include <GpuMemoryTracker.h>
+#include <utils/Trace.h>
+#include <array>
+#include <sstream>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+pthread_t gGpuThread = 0;
+
+#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount)
+
+const char* TYPE_NAMES[] = {
+        "Texture",
+        "OffscreenBuffer",
+        "Layer",
+};
+
+struct TypeStats {
+    int totalSize = 0;
+    int count = 0;
+};
+
+static std::array<TypeStats, NUM_TYPES> gObjectStats;
+static std::unordered_set<GpuMemoryTracker*> gObjectSet;
+
+void GpuMemoryTracker::notifySizeChanged(int newSize) {
+    int delta = newSize - mSize;
+    mSize = newSize;
+    gObjectStats[static_cast<int>(mType)].totalSize += delta;
+}
+
+void GpuMemoryTracker::startTrackingObject() {
+    auto result = gObjectSet.insert(this);
+    LOG_ALWAYS_FATAL_IF(!result.second,
+            "startTrackingObject() on %p failed, already being tracked!", this);
+    gObjectStats[static_cast<int>(mType)].count++;
+}
+
+void GpuMemoryTracker::stopTrackingObject() {
+    size_t removed = gObjectSet.erase(this);
+    LOG_ALWAYS_FATAL_IF(removed != 1,
+            "stopTrackingObject removed %zd, is %p not being tracked?",
+            removed, this);
+    gObjectStats[static_cast<int>(mType)].count--;
+}
+
+void GpuMemoryTracker::onGLContextCreated() {
+    LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a GL thread? "
+            "current = %lu, gl thread = %lu", pthread_self(), gGpuThread);
+    gGpuThread = pthread_self();
+}
+
+void GpuMemoryTracker::onGLContextDestroyed() {
+    gGpuThread = 0;
+    if (CC_UNLIKELY(gObjectSet.size() > 0)) {
+        std::stringstream os;
+        dump(os);
+        ALOGE("%s", os.str().c_str());
+        LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size());
+    }
+}
+
+void GpuMemoryTracker::dump() {
+    std::stringstream strout;
+    dump(strout);
+    ALOGD("%s", strout.str().c_str());
+}
+
+void GpuMemoryTracker::dump(std::ostream& stream) {
+    for (int type = 0; type < NUM_TYPES; type++) {
+        const TypeStats& stats = gObjectStats[type];
+        stream << TYPE_NAMES[type];
+        stream << " is using " << SizePrinter{stats.totalSize};
+        stream << ", count = " << stats.count;
+        stream << std::endl;
+    }
+}
+
+int GpuMemoryTracker::getInstanceCount(GpuObjectType type) {
+    return gObjectStats[static_cast<int>(type)].count;
+}
+
+int GpuMemoryTracker::getTotalSize(GpuObjectType type) {
+    return gObjectStats[static_cast<int>(type)].totalSize;
+}
+
+void GpuMemoryTracker::onFrameCompleted() {
+    if (ATRACE_ENABLED()) {
+        char buf[128];
+        for (int type = 0; type < NUM_TYPES; type++) {
+            snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]);
+            const TypeStats& stats = gObjectStats[type];
+            ATRACE_INT(buf, stats.totalSize);
+            snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]);
+            ATRACE_INT(buf, stats.count);
+        }
+    }
+
+    std::vector<const Texture*> freeList;
+    for (const auto& obj : gObjectSet) {
+        if (obj->objectType() == GpuObjectType::Texture) {
+            const Texture* texture = static_cast<Texture*>(obj);
+            if (texture->cleanup) {
+                ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u",
+                        texture->id(), texture->width(), texture->height());
+                freeList.push_back(texture);
+            }
+        }
+    }
+    for (auto& texture : freeList) {
+        const_cast<Texture*>(texture)->deleteTexture();
+        delete texture;
+    }
+}
+
+} // namespace uirenderer
+} // namespace android;
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
new file mode 100644
index 0000000..851aeae
--- /dev/null
+++ b/libs/hwui/GpuMemoryTracker.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <cutils/log.h>
+#include <pthread.h>
+#include <ostream>
+
+namespace android {
+namespace uirenderer {
+
+extern pthread_t gGpuThread;
+
+#define ASSERT_GPU_THREAD() LOG_ALWAYS_FATAL_IF( \
+        !pthread_equal(gGpuThread, pthread_self()), \
+        "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \
+        "!= gpu thread %lu", this, static_cast<int>(mType), mSize, \
+        pthread_self(), gGpuThread)
+
+enum class GpuObjectType {
+    Texture = 0,
+    OffscreenBuffer,
+    Layer,
+
+    TypeCount,
+};
+
+class GpuMemoryTracker {
+public:
+    GpuObjectType objectType() { return mType; }
+    int objectSize() { return mSize; }
+
+    static void onGLContextCreated();
+    static void onGLContextDestroyed();
+    static void dump();
+    static void dump(std::ostream& stream);
+    static int getInstanceCount(GpuObjectType type);
+    static int getTotalSize(GpuObjectType type);
+    static void onFrameCompleted();
+
+protected:
+    GpuMemoryTracker(GpuObjectType type) : mType(type) {
+        ASSERT_GPU_THREAD();
+        startTrackingObject();
+    }
+
+    ~GpuMemoryTracker() {
+        notifySizeChanged(0);
+        stopTrackingObject();
+    }
+
+    void notifySizeChanged(int newSize);
+
+private:
+    void startTrackingObject();
+    void stopTrackingObject();
+
+    int mSize = 0;
+    GpuObjectType mType;
+};
+
+} // namespace uirenderer
+} // namespace android;
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 8c46450..522aa96 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -110,9 +110,7 @@
 
 void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) {
     if (texture) {
-        const uint32_t size = texture->width * texture->height * bytesPerPixel();
-        mSize -= size;
-
+        mSize -= texture->objectSize();
         texture->deleteTexture();
         delete texture;
     }
@@ -167,18 +165,16 @@
     getGradientInfo(colors, count, info);
 
     Texture* texture = new Texture(Caches::getInstance());
-    texture->width = info.width;
-    texture->height = 2;
     texture->blend = info.hasAlpha;
     texture->generation = 1;
 
     // Asume the cache is always big enough
-    const uint32_t size = texture->width * texture->height * bytesPerPixel();
+    const uint32_t size = info.width * 2 * bytesPerPixel();
     while (getSize() + size > mMaxSize) {
         mCache.removeOldest();
     }
 
-    generateTexture(colors, positions, texture);
+    generateTexture(colors, positions, info.width, 2, texture);
 
     mSize += size;
     mCache.put(gradient, texture);
@@ -231,10 +227,10 @@
     dst += 4 * sizeof(float);
 }
 
-void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) {
-    const uint32_t width = texture->width;
+void GradientCache::generateTexture(uint32_t* colors, float* positions,
+        const uint32_t width, const uint32_t height, Texture* texture) {
     const GLsizei rowBytes = width * bytesPerPixel();
-    uint8_t pixels[rowBytes * texture->height];
+    uint8_t pixels[rowBytes * height];
 
     static ChannelSplitter gSplitters[] = {
             &android::uirenderer::GradientCache::splitToBytes,
@@ -277,17 +273,13 @@
 
     memcpy(pixels + rowBytes, pixels, rowBytes);
 
-    glGenTextures(1, &texture->id);
-    Caches::getInstance().textureState().bindTexture(texture->id);
     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
     if (mUseFloatTexture) {
         // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
-                GL_RGBA, GL_FLOAT, pixels);
+        texture->upload(width, height, GL_RGBA16F, GL_RGBA, GL_FLOAT, pixels);
     } else {
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
-                GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+        texture->upload(width, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
     }
 
     texture->setFilter(GL_LINEAR);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 7534c5d..b762ca7 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -143,7 +143,8 @@
     Texture* addLinearGradient(GradientCacheEntry& gradient,
             uint32_t* colors, float* positions, int count);
 
-    void generateTexture(uint32_t* colors, float* positions, Texture* texture);
+    void generateTexture(uint32_t* colors, float* positions,
+            const uint32_t width, const uint32_t height, Texture* texture);
 
     struct GradientInfo {
         uint32_t width;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 0fe20ad..8369266 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -36,7 +36,8 @@
 namespace uirenderer {
 
 Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
-        : state(State::Uncached)
+        : GpuMemoryTracker(GpuObjectType::Layer)
+        , state(State::Uncached)
         , caches(Caches::getInstance())
         , renderState(renderState)
         , texture(caches)
@@ -45,8 +46,8 @@
     // preserves the old inc/dec ref locations. This should be changed...
     incStrong(nullptr);
     renderTarget = GL_TEXTURE_2D;
-    texture.width = layerWidth;
-    texture.height = layerHeight;
+    texture.mWidth = layerWidth;
+    texture.mHeight = layerHeight;
     renderState.registerLayer(this);
 }
 
@@ -54,10 +55,9 @@
     renderState.unregisterLayer(this);
     SkSafeUnref(colorFilter);
 
-    if (stencil || fbo || texture.id) {
-        renderState.requireGLContext();
+    if (stencil || fbo || texture.mId) {
         removeFbo();
-        deleteTexture();
+        texture.deleteTexture();
     }
 
     delete[] mesh;
@@ -65,7 +65,7 @@
 
 void Layer::onGlContextLost() {
     removeFbo();
-    deleteTexture();
+    texture.deleteTexture();
 }
 
 uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
@@ -179,8 +179,8 @@
 }
 
 void Layer::bindTexture() const {
-    if (texture.id) {
-        caches.textureState().bindTexture(renderTarget, texture.id);
+    if (texture.mId) {
+        caches.textureState().bindTexture(renderTarget, texture.mId);
     }
 }
 
@@ -191,28 +191,22 @@
 }
 
 void Layer::generateTexture() {
-    if (!texture.id) {
-        glGenTextures(1, &texture.id);
-    }
-}
-
-void Layer::deleteTexture() {
-    if (texture.id) {
-        texture.deleteTexture();
-        texture.id = 0;
+    if (!texture.mId) {
+        glGenTextures(1, &texture.mId);
     }
 }
 
 void Layer::clearTexture() {
-    caches.textureState().unbindTexture(texture.id);
-    texture.id = 0;
+    caches.textureState().unbindTexture(texture.mId);
+    texture.mId = 0;
 }
 
 void Layer::allocateTexture() {
 #if DEBUG_LAYERS
     ALOGD("  Allocate layer: %dx%d", getWidth(), getHeight());
 #endif
-    if (texture.id) {
+    if (texture.mId) {
+        texture.updateSize(getWidth(), getHeight(), GL_RGBA);
         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
         glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e90f055..e00ae66 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -24,6 +24,7 @@
 #include <memory>
 
 #include <GLES2/gl2.h>
+#include <GpuMemoryTracker.h>
 
 #include <ui/Region.h>
 
@@ -54,7 +55,7 @@
 /**
  * A layer has dimensions and is backed by an OpenGL texture or FBO.
  */
-class Layer : public VirtualLightRefBase {
+class Layer : public VirtualLightRefBase, GpuMemoryTracker {
 public:
     enum class Type {
         Texture,
@@ -94,8 +95,8 @@
         regionRect.set(bounds.leftTop().x, bounds.leftTop().y,
                bounds.rightBottom().x, bounds.rightBottom().y);
 
-        const float texX = 1.0f / float(texture.width);
-        const float texY = 1.0f / float(texture.height);
+        const float texX = 1.0f / float(texture.mWidth);
+        const float texY = 1.0f / float(texture.mHeight);
         const float height = layer.getHeight();
         texCoords.set(
                regionRect.left * texX, (height - regionRect.top) * texY,
@@ -112,11 +113,11 @@
     void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
 
     inline uint32_t getWidth() const {
-        return texture.width;
+        return texture.mWidth;
     }
 
     inline uint32_t getHeight() const {
-        return texture.height;
+        return texture.mHeight;
     }
 
     /**
@@ -131,8 +132,7 @@
     bool resize(const uint32_t width, const uint32_t height);
 
     void setSize(uint32_t width, uint32_t height) {
-        texture.width = width;
-        texture.height = height;
+        texture.updateSize(width, height, texture.format());
     }
 
     ANDROID_API void setPaint(const SkPaint* paint);
@@ -201,7 +201,7 @@
     }
 
     inline GLuint getTextureId() const {
-        return texture.id;
+        return texture.id();
     }
 
     inline Texture& getTexture() {
@@ -263,7 +263,6 @@
     void bindTexture() const;
     void generateTexture();
     void allocateTexture();
-    void deleteTexture();
 
     /**
      * When the caller frees the texture itself, the caller
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
new file mode 100644
index 0000000..7170d4f
--- /dev/null
+++ b/libs/hwui/LayerBuilder.cpp
@@ -0,0 +1,365 @@
+/*
+ * 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 "LayerBuilder.h"
+
+#include "BakedOpState.h"
+#include "RenderNode.h"
+#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace uirenderer {
+
+class BatchBase {
+public:
+    BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
+            : mBatchId(batchId)
+            , mMerging(merging) {
+        mBounds = op->computedState.clippedBounds;
+        mOps.push_back(op);
+    }
+
+    bool intersects(const Rect& rect) const {
+        if (!rect.intersects(mBounds)) return false;
+
+        for (const BakedOpState* op : mOps) {
+            if (rect.intersects(op->computedState.clippedBounds)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    batchid_t getBatchId() const { return mBatchId; }
+    bool isMerging() const { return mMerging; }
+
+    const std::vector<BakedOpState*>& getOps() const { return mOps; }
+
+    void dump() const {
+        ALOGD("    Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
+                this, mBatchId, mMerging, (int) mOps.size(), RECT_ARGS(mBounds));
+    }
+protected:
+    batchid_t mBatchId;
+    Rect mBounds;
+    std::vector<BakedOpState*> mOps;
+    bool mMerging;
+};
+
+class OpBatch : public BatchBase {
+public:
+    static void* operator new(size_t size, LinearAllocator& allocator) {
+        return allocator.alloc(size);
+    }
+
+    OpBatch(batchid_t batchId, BakedOpState* op)
+            : BatchBase(batchId, op, false) {
+    }
+
+    void batchOp(BakedOpState* op) {
+        mBounds.unionWith(op->computedState.clippedBounds);
+        mOps.push_back(op);
+    }
+};
+
+class MergingOpBatch : public BatchBase {
+public:
+    static void* operator new(size_t size, LinearAllocator& allocator) {
+        return allocator.alloc(size);
+    }
+
+    MergingOpBatch(batchid_t batchId, BakedOpState* op)
+            : BatchBase(batchId, op, true)
+            , mClipSideFlags(op->computedState.clipSideFlags) {
+    }
+
+    /*
+     * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
+     * and clip side flags. Positive bounds delta means new bounds fit in old.
+     */
+    static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
+            float boundsDelta) {
+        bool currentClipExists = currentFlags & side;
+        bool newClipExists = newFlags & side;
+
+        // if current is clipped, we must be able to fit new bounds in current
+        if (boundsDelta > 0 && currentClipExists) return false;
+
+        // if new is clipped, we must be able to fit current bounds in new
+        if (boundsDelta < 0 && newClipExists) return false;
+
+        return true;
+    }
+
+    static bool paintIsDefault(const SkPaint& paint) {
+        return paint.getAlpha() == 255
+                && paint.getColorFilter() == nullptr
+                && paint.getShader() == nullptr;
+    }
+
+    static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
+        // Note: don't check color, since all currently mergeable ops can merge across colors
+        return a.getAlpha() == b.getAlpha()
+                && a.getColorFilter() == b.getColorFilter()
+                && a.getShader() == b.getShader();
+    }
+
+    /*
+     * Checks if a (mergeable) op can be merged into this batch
+     *
+     * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
+     * important to consider all paint attributes used in the draw calls in deciding both a) if an
+     * op tries to merge at all, and b) if the op can merge with another set of ops
+     *
+     * False positives can lead to information from the paints of subsequent merged operations being
+     * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
+     */
+    bool canMergeWith(BakedOpState* op) const {
+        bool isTextBatch = getBatchId() == OpBatchType::Text
+                || getBatchId() == OpBatchType::ColorText;
+
+        // Overlapping other operations is only allowed for text without shadow. For other ops,
+        // multiDraw isn't guaranteed to overdraw correctly
+        if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
+            if (intersects(op->computedState.clippedBounds)) return false;
+        }
+
+        const BakedOpState* lhs = op;
+        const BakedOpState* rhs = mOps[0];
+
+        if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
+
+        // Identical round rect clip state means both ops will clip in the same way, or not at all.
+        // As the state objects are const, we can compare their pointers to determine mergeability
+        if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
+        if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
+
+        /* Clipping compatibility check
+         *
+         * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
+         * clip for that side.
+         */
+        const int currentFlags = mClipSideFlags;
+        const int newFlags = op->computedState.clipSideFlags;
+        if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
+            const Rect& opBounds = op->computedState.clippedBounds;
+            float boundsDelta = mBounds.left - opBounds.left;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
+            boundsDelta = mBounds.top - opBounds.top;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
+
+            // right and bottom delta calculation reversed to account for direction
+            boundsDelta = opBounds.right - mBounds.right;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
+            boundsDelta = opBounds.bottom - mBounds.bottom;
+            if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
+        }
+
+        const SkPaint* newPaint = op->op->paint;
+        const SkPaint* oldPaint = mOps[0]->op->paint;
+
+        if (newPaint == oldPaint) {
+            // if paints are equal, then modifiers + paint attribs don't need to be compared
+            return true;
+        } else if (newPaint && !oldPaint) {
+            return paintIsDefault(*newPaint);
+        } else if (!newPaint && oldPaint) {
+            return paintIsDefault(*oldPaint);
+        }
+        return paintsAreEquivalent(*newPaint, *oldPaint);
+    }
+
+    void mergeOp(BakedOpState* op) {
+        mBounds.unionWith(op->computedState.clippedBounds);
+        mOps.push_back(op);
+
+        // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
+        // check, and doesn't extend past a side of the clip that's in use by the merged batch.
+        // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
+        mClipSideFlags |= op->computedState.clipSideFlags;
+    }
+
+    int getClipSideFlags() const { return mClipSideFlags; }
+    const Rect& getClipRect() const { return mBounds; }
+
+private:
+    int mClipSideFlags;
+};
+
+LayerBuilder::LayerBuilder(uint32_t width, uint32_t height,
+        const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+        : width(width)
+        , height(height)
+        , repaintRect(repaintRect)
+        , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
+        , beginLayerOp(beginLayerOp)
+        , renderNode(renderNode)
+        , viewportClip(Rect(width, height)) {}
+
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still iterate to find similar batch to insert after
+void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds,
+        BatchBase** targetBatch, size_t* insertBatchIndex) const {
+    for (int i = mBatches.size() - 1; i >= 0; i--) {
+        BatchBase* overBatch = mBatches[i];
+
+        if (overBatch == *targetBatch) break;
+
+        // TODO: also consider shader shared between batch types
+        if (batchId == overBatch->getBatchId()) {
+            *insertBatchIndex = i + 1;
+            if (!*targetBatch) break; // found insert position, quit
+        }
+
+        if (overBatch->intersects(clippedBounds)) {
+            // NOTE: it may be possible to optimize for special cases where two operations
+            // of the same batch/paint could swap order, such as with a non-mergeable
+            // (clipped) and a mergeable text operation
+            *targetBatch = nullptr;
+            break;
+        }
+    }
+}
+
+void LayerBuilder::deferLayerClear(const Rect& rect) {
+    mClearRects.push_back(rect);
+}
+
+void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
+    if (CC_UNLIKELY(!mClearRects.empty())) {
+        const int vertCount = mClearRects.size() * 4;
+        // put the verts in the frame allocator, since
+        //     1) SimpleRectsOps needs verts, not rects
+        //     2) even if mClearRects stored verts, std::vectors will move their contents
+        Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
+
+        Vertex* currentVert = verts;
+        Rect bounds = mClearRects[0];
+        for (auto&& rect : mClearRects) {
+            bounds.unionWith(rect);
+            Vertex::set(currentVert++, rect.left, rect.top);
+            Vertex::set(currentVert++, rect.right, rect.top);
+            Vertex::set(currentVert++, rect.left, rect.bottom);
+            Vertex::set(currentVert++, rect.right, rect.bottom);
+        }
+        mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
+
+        // One or more unclipped saveLayers have been enqueued, with deferred clears.
+        // Flush all of these clears with a single draw
+        SkPaint* paint = allocator.create<SkPaint>();
+        paint->setXfermodeMode(SkXfermode::kClear_Mode);
+        SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
+                Matrix4::identity(), nullptr, paint,
+                verts, vertCount);
+        BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
+                &viewportClip, bounds, *op);
+        deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
+    }
+}
+
+void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
+        BakedOpState* op, batchid_t batchId) {
+    if (batchId != OpBatchType::CopyToLayer) {
+        // if first op after one or more unclipped saveLayers, flush the layer clears
+        flushLayerClears(allocator);
+    }
+
+    OpBatch* targetBatch = mBatchLookup[batchId];
+
+    size_t insertBatchIndex = mBatches.size();
+    if (targetBatch) {
+        locateInsertIndex(batchId, op->computedState.clippedBounds,
+                (BatchBase**)(&targetBatch), &insertBatchIndex);
+    }
+
+    if (targetBatch) {
+        targetBatch->batchOp(op);
+    } else  {
+        // new non-merging batch
+        targetBatch = new (allocator) OpBatch(batchId, op);
+        mBatchLookup[batchId] = targetBatch;
+        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+    }
+}
+
+void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
+        BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+    if (batchId != OpBatchType::CopyToLayer) {
+        // if first op after one or more unclipped saveLayers, flush the layer clears
+        flushLayerClears(allocator);
+    }
+    MergingOpBatch* targetBatch = nullptr;
+
+    // Try to merge with any existing batch with same mergeId
+    auto getResult = mMergingBatchLookup[batchId].find(mergeId);
+    if (getResult != mMergingBatchLookup[batchId].end()) {
+        targetBatch = getResult->second;
+        if (!targetBatch->canMergeWith(op)) {
+            targetBatch = nullptr;
+        }
+    }
+
+    size_t insertBatchIndex = mBatches.size();
+    locateInsertIndex(batchId, op->computedState.clippedBounds,
+            (BatchBase**)(&targetBatch), &insertBatchIndex);
+
+    if (targetBatch) {
+        targetBatch->mergeOp(op);
+    } else  {
+        // new merging batch
+        targetBatch = new (allocator) MergingOpBatch(batchId, op);
+        mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+        mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+    }
+}
+
+void LayerBuilder::replayBakedOpsImpl(void* arg,
+        BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
+    ATRACE_NAME("flush drawing commands");
+    for (const BatchBase* batch : mBatches) {
+        size_t size = batch->getOps().size();
+        if (size > 1 && batch->isMerging()) {
+            int opId = batch->getOps()[0]->op->opId;
+            const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
+            MergedBakedOpList data = {
+                    batch->getOps().data(),
+                    size,
+                    mergingBatch->getClipSideFlags(),
+                    mergingBatch->getClipRect()
+            };
+            mergedReceivers[opId](arg, data);
+        } else {
+            for (const BakedOpState* op : batch->getOps()) {
+                unmergedReceivers[op->op->opId](arg, *op);
+            }
+        }
+    }
+}
+
+void LayerBuilder::dump() const {
+    ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p",
+            this, width, height, offscreenBuffer, beginLayerOp, renderNode);
+    for (const BatchBase* batch : mBatches) {
+        batch->dump();
+    }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h
new file mode 100644
index 0000000..99968e1
--- /dev/null
+++ b/libs/hwui/LayerBuilder.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "ClipArea.h"
+#include "Rect.h"
+#include "utils/Macros.h"
+
+#include <vector>
+#include <unordered_map>
+
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class BakedOpState;
+struct BeginLayerOp;
+class BatchBase;
+class LinearAllocator;
+struct MergedBakedOpList;
+class MergingOpBatch;
+class OffscreenBuffer;
+class OpBatch;
+class RenderNode;
+
+typedef int batchid_t;
+typedef const void* mergeid_t;
+
+namespace OpBatchType {
+    enum {
+        Bitmap,
+        MergedPatch,
+        AlphaVertices,
+        Vertices,
+        AlphaMaskTexture,
+        Text,
+        ColorText,
+        Shadow,
+        TextureLayer,
+        Functor,
+        CopyToLayer,
+        CopyFromLayer,
+
+        Count // must be last
+    };
+}
+
+typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
+typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
+
+/**
+ * Stores the deferred render operations and state used to compute ordering
+ * for a single FBO/layer.
+ */
+class LayerBuilder {
+// Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip
+PREVENT_COPY_AND_ASSIGN(LayerBuilder);
+public:
+    // Create LayerBuilder for Fbo0
+    LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect)
+            : LayerBuilder(width, height, repaintRect, nullptr, nullptr) {};
+
+    // Create LayerBuilder for an offscreen layer, where beginLayerOp is present for a
+    // saveLayer, renderNode is present for a HW layer.
+    LayerBuilder(uint32_t width, uint32_t height,
+            const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+
+    // iterate back toward target to see if anything drawn since should overlap the new op
+    // if no target, merging ops still iterate to find similar batch to insert after
+    void locateInsertIndex(int batchId, const Rect& clippedBounds,
+            BatchBase** targetBatch, size_t* insertBatchIndex) const;
+
+    void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
+
+    // insertion point of a new batch, will hopefully be immediately after similar batch
+    // (generally, should be similar shader)
+    void deferMergeableOp(LinearAllocator& allocator,
+            BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
+
+    void deferLayerClear(const Rect& dstRect);
+
+    bool empty() const {
+        return mBatches.empty();
+    }
+
+    void clear() {
+        mBatches.clear();
+    }
+
+    void dump() const;
+
+    const uint32_t width;
+    const uint32_t height;
+    const Rect repaintRect;
+    OffscreenBuffer* offscreenBuffer;
+    const BeginLayerOp* beginLayerOp;
+    const RenderNode* renderNode;
+    const ClipRect viewportClip;
+
+    // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
+    std::vector<BakedOpState*> activeUnclippedSaveLayers;
+private:
+    void flushLayerClears(LinearAllocator& allocator);
+
+    std::vector<BatchBase*> mBatches;
+
+    /**
+     * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+     * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+     * collide, which avoids the need to resolve mergeid collisions.
+     */
+    std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
+
+    // Maps batch ids to the most recent *non-merging* batch of that id
+    OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+
+    std::vector<Rect> mClearRects;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index b117754..f5681ce 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -112,7 +112,6 @@
         layer->bindTexture();
         layer->setFilter(GL_NEAREST);
         layer->setWrap(GL_CLAMP_TO_EDGE, false);
-        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
 #if DEBUG_LAYERS
         dump();
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index be612d2..5b1a854 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -42,7 +42,7 @@
     LayerUpdateQueue() {}
     void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
     void clear();
-    const std::vector<Entry> entries() const { return mEntries; }
+    const std::vector<Entry>& entries() const { return mEntries; }
 private:
     std::vector<Entry> mEntries;
 };
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
deleted file mode 100644
index dbbce8b..0000000
--- a/libs/hwui/OpReorderer.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef ANDROID_HWUI_OP_REORDERER_H
-#define ANDROID_HWUI_OP_REORDERER_H
-
-#include "BakedOpState.h"
-#include "CanvasState.h"
-#include "DisplayList.h"
-#include "RecordedOp.h"
-
-#include <vector>
-#include <unordered_map>
-
-struct SkRect;
-
-namespace android {
-namespace uirenderer {
-
-class BakedOpState;
-class BatchBase;
-class LayerUpdateQueue;
-class MergingOpBatch;
-class OffscreenBuffer;
-class OpBatch;
-class Rect;
-
-typedef int batchid_t;
-typedef const void* mergeid_t;
-
-namespace OpBatchType {
-    enum {
-        Bitmap,
-        MergedPatch,
-        AlphaVertices,
-        Vertices,
-        AlphaMaskTexture,
-        Text,
-        ColorText,
-        Shadow,
-        TextureLayer,
-        Functor,
-
-        Count // must be last
-    };
-}
-
-class OpReorderer : public CanvasStateClient {
-    typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
-    typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
-
-    /**
-     * Stores the deferred render operations and state used to compute ordering
-     * for a single FBO/layer.
-     */
-    class LayerReorderer {
-    public:
-        // Create LayerReorderer for Fbo0
-        LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
-                : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
-
-        // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
-        // saveLayer, renderNode is present for a HW layer.
-        LayerReorderer(uint32_t width, uint32_t height,
-                const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
-
-        // iterate back toward target to see if anything drawn since should overlap the new op
-        // if no target, merging ops still iterate to find similar batch to insert after
-        void locateInsertIndex(int batchId, const Rect& clippedBounds,
-                BatchBase** targetBatch, size_t* insertBatchIndex) const;
-
-        void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
-
-        // insertion point of a new batch, will hopefully be immediately after similar batch
-        // (generally, should be similar shader)
-        void deferMergeableOp(LinearAllocator& allocator,
-                BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
-
-        void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
-
-        bool empty() const {
-            return mBatches.empty();
-        }
-
-        void clear() {
-            mBatches.clear();
-        }
-
-        void dump() const;
-
-        const uint32_t width;
-        const uint32_t height;
-        const Rect repaintRect;
-        OffscreenBuffer* offscreenBuffer;
-        const BeginLayerOp* beginLayerOp;
-        const RenderNode* renderNode;
-    private:
-        std::vector<BatchBase*> mBatches;
-
-        /**
-         * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
-         * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
-         * collide, which avoids the need to resolve mergeid collisions.
-         */
-        std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
-
-        // Maps batch ids to the most recent *non-merging* batch of that id
-        OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
-    };
-
-public:
-    OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
-            uint32_t viewportWidth, uint32_t viewportHeight,
-            const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
-
-    virtual ~OpReorderer() {}
-
-    /**
-     * replayBakedOps() is templated based on what class will receive ops being replayed.
-     *
-     * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
-     * state->op->opId to lookup a receiver that will be called when the op is replayed.
-     *
-     */
-    template <typename StaticDispatcher, typename Renderer>
-    void replayBakedOps(Renderer& renderer) {
-        /**
-         * defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to
-         * dispatch the op via a method on a static dispatcher when the op is replayed.
-         *
-         * For example a BitmapOp would resolve, via the lambda lookup, to calling:
-         *
-         * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state);
-         */
-        #define X(Type) \
-                [](void* renderer, const BakedOpState& state) { \
-                    StaticDispatcher::on##Type(*(static_cast<Renderer*>(renderer)), static_cast<const Type&>(*(state.op)), state); \
-                },
-        static BakedOpReceiver unmergedReceivers[] = {
-            MAP_OPS(X)
-        };
-        #undef X
-
-        /**
-         * defines a LUT of lambdas which allow merged arrays of BakedOpState* to be passed to a
-         * static dispatcher when the group of merged ops is replayed. Unmergeable ops trigger
-         * a LOG_ALWAYS_FATAL().
-         */
-        #define X(Type) \
-                [](void* renderer, const MergedBakedOpList& opList) { \
-                    LOG_ALWAYS_FATAL("op type %d does not support merging", opList.states[0]->op->opId); \
-                },
-        #define Y(Type) \
-                [](void* renderer, const MergedBakedOpList& opList) { \
-                    StaticDispatcher::onMerged##Type##s(*(static_cast<Renderer*>(renderer)), opList); \
-                },
-        static MergedOpReceiver mergedReceivers[] = {
-            MAP_OPS_BASED_ON_MERGEABILITY(X, Y)
-        };
-        #undef X
-        #undef Y
-
-        // Relay through layers in reverse order, since layers
-        // later in the list will be drawn by earlier ones
-        for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
-            LayerReorderer& layer = mLayerReorderers[i];
-            if (layer.renderNode) {
-                // cached HW layer - can't skip layer if empty
-                renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
-                layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
-                renderer.endLayer();
-            } else if (!layer.empty()) { // save layer - skip entire layer if empty
-                layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
-                layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
-                renderer.endLayer();
-            }
-        }
-
-        const LayerReorderer& fbo0 = mLayerReorderers[0];
-        renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
-        fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
-        renderer.endFrame();
-    }
-
-    void dump() const {
-        for (auto&& layer : mLayerReorderers) {
-            layer.dump();
-        }
-    }
-
-    ///////////////////////////////////////////////////////////////////
-    /// CanvasStateClient interface
-    ///////////////////////////////////////////////////////////////////
-    virtual void onViewportInitialized() override;
-    virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
-    virtual GLuint getTargetFbo() const override { return 0; }
-
-private:
-    enum class ChildrenSelectMode {
-        Negative,
-        Positive
-    };
-    void saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
-            float contentTranslateX, float contentTranslateY,
-            const Rect& repaintRect,
-            const Vector3& lightCenter,
-            const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
-    void restoreForLayer();
-
-    LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
-
-    BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
-        return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
-    }
-
-    // should always be surrounded by a save/restore pair, and not called if DisplayList is null
-    void deferNodePropsAndOps(RenderNode& node);
-
-    template <typename V>
-    void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes);
-
-    void deferShadow(const RenderNodeOp& casterOp);
-
-    void deferProjectedChildren(const RenderNode& renderNode);
-
-    void deferNodeOps(const RenderNode& renderNode);
-
-    void deferRenderNodeOpImpl(const RenderNodeOp& op);
-
-    void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
-
-    SkPath* createFrameAllocatedPath() {
-        mFrameAllocatedPaths.emplace_back(new SkPath);
-        return mFrameAllocatedPaths.back().get();
-    }
-
-    void deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
-            BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
-
-    /**
-     * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type.
-     *
-     * These private methods are called from within deferImpl to defer each individual op
-     * type differently.
-     */
-#define INTERNAL_OP_HANDLER(Type) \
-    void defer##Type(const Type& op);
-    MAP_OPS(INTERNAL_OP_HANDLER)
-
-    std::vector<std::unique_ptr<SkPath> > mFrameAllocatedPaths;
-
-    // List of every deferred layer's render state. Replayed in reverse order to render a frame.
-    std::vector<LayerReorderer> mLayerReorderers;
-
-    /*
-     * Stack of indices within mLayerReorderers representing currently active layers. If drawing
-     * layerA within a layerB, will contain, in order:
-     *  - 0 (representing FBO 0, always present)
-     *  - layerB's index
-     *  - layerA's index
-     *
-     * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
-     * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
-     * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
-     * ops added to it.
-    */
-    std::vector<size_t> mLayerStack;
-
-    CanvasState mCanvasState;
-
-    // contains ResolvedOps and Batches
-    LinearAllocator mAllocator;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_OP_REORDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 92b758d..0cd763d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <GpuMemoryTracker.h>
 #include "OpenGLRenderer.h"
 
 #include "DeferredDisplayList.h"
@@ -200,6 +201,7 @@
 
 #if DEBUG_MEMORY_USAGE
         mCaches.dumpMemoryUsage();
+        GPUMemoryTracker::dump();
 #else
         if (Properties::debugLevel & kDebugMemory) {
             mCaches.dumpMemoryUsage();
@@ -1497,7 +1499,7 @@
             .setMeshTexturedUnitQuad(texture->uvMapper)
             .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
             .setTransform(*currentSnapshot(),  TransformFlags::None)
-            .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
+            .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
             .build();
     renderGlop(glop);
 }
@@ -1601,10 +1603,10 @@
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
-    Rect uv(std::max(0.0f, src.left / texture->width),
-            std::max(0.0f, src.top / texture->height),
-            std::min(1.0f, src.right / texture->width),
-            std::min(1.0f, src.bottom / texture->height));
+    Rect uv(std::max(0.0f, src.left / texture->width()),
+            std::max(0.0f, src.top / texture->height()),
+            std::min(1.0f, src.right / texture->width()),
+            std::min(1.0f, src.bottom / texture->height()));
 
     const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
             ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
@@ -1977,7 +1979,7 @@
             .setMeshTexturedUnitQuad(nullptr)
             .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
             .setTransform(*currentSnapshot(),  TransformFlags::None)
-            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+            .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
             .build();
     renderGlop(glop);
 }
@@ -2316,7 +2318,7 @@
 
 void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
         const SkPaint* paint) {
-    if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
+    if (quickRejectSetupScissor(x, y, x + texture->width(), y + texture->height())) {
         return;
     }
 
@@ -2326,7 +2328,7 @@
             .setMeshTexturedUnitQuad(nullptr)
             .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
             .setTransform(*currentSnapshot(),  TransformFlags::None)
-            .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
+            .setModelViewMapUnitToRect(Rect(x, y, x + texture->width(), y + texture->height()))
             .build();
     renderGlop(glop);
 }
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 06ea55a..bfabc1d 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -185,7 +185,7 @@
 
 void PathCache::removeTexture(PathTexture* texture) {
     if (texture) {
-        const uint32_t size = texture->width * texture->height;
+        const uint32_t size = texture->width() * texture->height();
 
         // If there is a pending task we must wait for it to return
         // before attempting our cleanup
@@ -209,9 +209,7 @@
             ALOGD("Shape deleted, size = %d", size);
         }
 
-        if (texture->id) {
-            Caches::getInstance().textureState().deleteTexture(texture->id);
-        }
+        texture->deleteTexture();
         delete texture;
     }
 }
@@ -248,8 +246,7 @@
     drawPath(path, paint, bitmap, left, top, offset, width, height);
 
     PathTexture* texture = new PathTexture(Caches::getInstance(),
-            left, top, offset, width, height,
-            path->getGenerationID());
+            left, top, offset, path->getGenerationID());
     generateTexture(entry, &bitmap, texture);
 
     return texture;
@@ -262,7 +259,7 @@
     // Note here that we upload to a texture even if it's bigger than mMaxSize.
     // Such an entry in mCache will only be temporary, since it will be evicted
     // immediately on trim, or on any other Path entering the cache.
-    uint32_t size = texture->width * texture->height;
+    uint32_t size = texture->width() * texture->height();
     mSize += size;
     PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
             texture->id, size, mSize);
@@ -280,24 +277,8 @@
 
 void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
     ATRACE_NAME("Upload Path Texture");
-    SkAutoLockPixels alp(bitmap);
-    if (!bitmap.readyToDraw()) {
-        ALOGE("Cannot generate texture from bitmap");
-        return;
-    }
-
-    glGenTextures(1, &texture->id);
-
-    Caches::getInstance().textureState().bindTexture(texture->id);
-    // Textures are Alpha8
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
-    texture->blend = true;
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
-            GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
-
+    texture->upload(bitmap);
     texture->setFilter(GL_LINEAR);
-    texture->setWrap(GL_CLAMP_TO_EDGE);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -320,16 +301,12 @@
     texture->left = left;
     texture->top = top;
     texture->offset = offset;
-    texture->width = width;
-    texture->height = height;
 
     if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
         SkBitmap* bitmap = new SkBitmap();
         drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
         t->setResult(bitmap);
     } else {
-        texture->width = 0;
-        texture->height = 0;
         t->setResult(nullptr);
     }
 }
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 302e9f8..18f380f 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -61,13 +61,11 @@
  */
 struct PathTexture: public Texture {
     PathTexture(Caches& caches, float left, float top,
-            float offset, int width, int height, int generation)
+            float offset, int generation)
             : Texture(caches)
             , left(left)
             , top(top)
             , offset(offset) {
-        this->width = width;
-        this->height = height;
         this->generation = generation;
     }
     PathTexture(Caches& caches, int generation)
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index cfdd0d2..30d5c29 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -33,68 +33,116 @@
 namespace android {
 namespace uirenderer {
 
+struct ClipBase;
 class OffscreenBuffer;
 class RenderNode;
 struct Vertex;
 
 /**
- * On of the provided macros is executed for each op type in order. The first will be used for ops
- * that cannot merge, and the second for those that can.
+ * Authoritative op list, used for generating the op ID enum, ID based LUTS, and
+ * the functions to which they dispatch. Parameter macros are executed for each op,
+ * in order, based on the op's type.
  *
- * This serves as the authoritative list of ops, used for generating ID enum, and ID based LUTs.
+ * There are 4 types of op, which defines dispatch/LUT capability:
+ *
+ *              | DisplayList |   Render    |    Merge    |
+ * -------------|-------------|-------------|-------------|
+ * PRE RENDER   |     Yes     |             |             |
+ * RENDER ONLY  |             |     Yes     |             |
+ * UNMERGEABLE  |     Yes     |     Yes     |             |
+ * MERGEABLE    |     Yes     |     Yes     |     Yes     |
+ *
+ * PRE RENDER - These ops are recorded into DisplayLists, but can't be directly rendered. This
+ *      may be because they need to be transformed into other op types (e.g. CirclePropsOp),
+ *      be traversed to access multiple renderable ops within (e.g. RenderNodeOp), or because they
+ *      modify renderbuffer lifecycle, instead of directly rendering content (the various LayerOps).
+ *
+ * RENDER ONLY - These ops cannot be recorded into DisplayLists, and are instead implicitly
+ *      constructed from other commands/RenderNode properties. They cannot be merged.
+ *
+ * UNMERGEABLE - These ops can be recorded into DisplayLists and rendered directly, but do not
+ *      support merged rendering.
+ *
+ * MERGEABLE - These ops can be recorded into DisplayLists and rendered individually, or merged
+ *      under certain circumstances.
  */
-#define MAP_OPS_BASED_ON_MERGEABILITY(U_OP_FN, M_OP_FN) \
-        U_OP_FN(ArcOp) \
-        M_OP_FN(BitmapOp) \
-        U_OP_FN(BitmapMeshOp) \
-        U_OP_FN(BitmapRectOp) \
-        U_OP_FN(CirclePropsOp) \
-        U_OP_FN(FunctorOp) \
-        U_OP_FN(LinesOp) \
-        U_OP_FN(OvalOp) \
-        M_OP_FN(PatchOp) \
-        U_OP_FN(PathOp) \
-        U_OP_FN(PointsOp) \
-        U_OP_FN(RectOp) \
-        U_OP_FN(RenderNodeOp) \
-        U_OP_FN(RoundRectOp) \
-        U_OP_FN(RoundRectPropsOp) \
-        U_OP_FN(ShadowOp) \
-        U_OP_FN(SimpleRectsOp) \
-        M_OP_FN(TextOp) \
-        U_OP_FN(TextOnPathOp) \
-        U_OP_FN(TextureLayerOp) \
-        U_OP_FN(BeginLayerOp) \
-        U_OP_FN(EndLayerOp) \
-        U_OP_FN(LayerOp)
+#define MAP_OPS_BASED_ON_TYPE(PRE_RENDER_OP_FN, RENDER_ONLY_OP_FN, UNMERGEABLE_OP_FN, MERGEABLE_OP_FN) \
+        PRE_RENDER_OP_FN(RenderNodeOp) \
+        PRE_RENDER_OP_FN(CirclePropsOp) \
+        PRE_RENDER_OP_FN(RoundRectPropsOp) \
+        PRE_RENDER_OP_FN(BeginLayerOp) \
+        PRE_RENDER_OP_FN(EndLayerOp) \
+        PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \
+        PRE_RENDER_OP_FN(EndUnclippedLayerOp) \
+        \
+        RENDER_ONLY_OP_FN(ShadowOp) \
+        RENDER_ONLY_OP_FN(LayerOp) \
+        RENDER_ONLY_OP_FN(CopyToLayerOp) \
+        RENDER_ONLY_OP_FN(CopyFromLayerOp) \
+        \
+        UNMERGEABLE_OP_FN(ArcOp) \
+        UNMERGEABLE_OP_FN(BitmapMeshOp) \
+        UNMERGEABLE_OP_FN(BitmapRectOp) \
+        UNMERGEABLE_OP_FN(FunctorOp) \
+        UNMERGEABLE_OP_FN(LinesOp) \
+        UNMERGEABLE_OP_FN(OvalOp) \
+        UNMERGEABLE_OP_FN(PathOp) \
+        UNMERGEABLE_OP_FN(PointsOp) \
+        UNMERGEABLE_OP_FN(RectOp) \
+        UNMERGEABLE_OP_FN(RoundRectOp) \
+        UNMERGEABLE_OP_FN(SimpleRectsOp) \
+        UNMERGEABLE_OP_FN(TextOnPathOp) \
+        UNMERGEABLE_OP_FN(TextureLayerOp) \
+        \
+        MERGEABLE_OP_FN(BitmapOp) \
+        MERGEABLE_OP_FN(PatchOp) \
+        MERGEABLE_OP_FN(TextOp)
 
 /**
- * The provided macro is executed for each op type in order. This is used in cases where
- * merge-ability of ops doesn't matter.
+ * LUT generators, which will insert nullptr for unsupported ops
  */
-#define MAP_OPS(OP_FN) \
-        MAP_OPS_BASED_ON_MERGEABILITY(OP_FN, OP_FN)
+#define NULLPTR_OP_FN(Type) nullptr,
 
+#define BUILD_DEFERRABLE_OP_LUT(OP_FN) \
+        { MAP_OPS_BASED_ON_TYPE(OP_FN, NULLPTR_OP_FN, OP_FN, OP_FN) }
+
+#define BUILD_MERGEABLE_OP_LUT(OP_FN) \
+        { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, NULLPTR_OP_FN, NULLPTR_OP_FN, OP_FN) }
+
+#define BUILD_RENDERABLE_OP_LUT(OP_FN) \
+        { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, OP_FN, OP_FN, OP_FN) }
+
+/**
+ * Op mapping functions, which skip unsupported ops.
+ *
+ * Note: Do not use for LUTS, since these do not preserve ID order.
+ */
 #define NULL_OP_FN(Type)
 
-#define MAP_MERGED_OPS(OP_FN) \
-        MAP_OPS_BASED_ON_MERGEABILITY(NULL_OP_FN, OP_FN)
+#define MAP_DEFERRABLE_OPS(OP_FN) \
+        MAP_OPS_BASED_ON_TYPE(OP_FN, NULL_OP_FN, OP_FN, OP_FN)
+
+#define MAP_MERGEABLE_OPS(OP_FN) \
+        MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, NULL_OP_FN, NULL_OP_FN, OP_FN)
+
+#define MAP_RENDERABLE_OPS(OP_FN) \
+        MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, OP_FN, OP_FN, OP_FN)
 
 // Generate OpId enum
 #define IDENTITY_FN(Type) Type,
 namespace RecordedOpId {
     enum {
-        MAP_OPS(IDENTITY_FN)
+        MAP_OPS_BASED_ON_TYPE(IDENTITY_FN, IDENTITY_FN, IDENTITY_FN, IDENTITY_FN)
         Count,
     };
 }
-static_assert(RecordedOpId::ArcOp == 0,
+static_assert(RecordedOpId::RenderNodeOp == 0,
         "First index must be zero for LUTs to work");
 
-#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint
-#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect
-#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, paint)
-#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, nullptr)
+#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint
+#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const ClipBase* localClip
+#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, paint)
+#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, nullptr)
 
 struct RecordedOp {
     /* ID from RecordedOpId - generally used for jumping into function tables */
@@ -106,8 +154,8 @@
     /* transform in recording space (vs DisplayList origin) */
     const Matrix4 localMatrix;
 
-    /* clip in recording space */
-    const Rect localClipRect;
+    /* clip in recording space - nullptr if not clipped */
+    const ClipBase* localClip;
 
     /* optional paint, stored in base object to simplify merging logic */
     const SkPaint* paint;
@@ -116,7 +164,7 @@
             : opId(opId)
             , unmappedBounds(unmappedBounds)
             , localMatrix(localMatrix)
-            , localClipRect(localClipRect)
+            , localClip(localClip)
             , paint(paint) {}
 };
 
@@ -187,9 +235,9 @@
 };
 
 struct CirclePropsOp : RecordedOp {
-    CirclePropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+    CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
             float* x, float* y, float* radius)
-            : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClipRect, paint)
+            : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint)
             , x(x)
             , y(y)
             , radius(radius) {}
@@ -259,9 +307,9 @@
 };
 
 struct RoundRectPropsOp : RecordedOp {
-    RoundRectPropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+    RoundRectPropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
             float* left, float* top, float* right, float* bottom, float *rx, float *ry)
-            : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClipRect, paint)
+            : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClip, paint)
             , left(left)
             , top(top)
             , right(right)
@@ -286,12 +334,13 @@
  */
 struct ShadowOp : RecordedOp {
     ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath,
-            const Rect& clipRect, const Vector3& lightCenter)
-            : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), clipRect, nullptr)
+            const Rect& localClipRect, const Vector3& lightCenter)
+            : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), nullptr, nullptr)
             , shadowMatrixXY(casterOp.localMatrix)
             , shadowMatrixZ(casterOp.localMatrix)
             , casterAlpha(casterAlpha)
             , casterPath(casterPath)
+            , localClipRect(localClipRect)
             , lightCenter(lightCenter) {
         const RenderNode& node = *casterOp.renderNode;
         node.applyViewPropertyTransforms(shadowMatrixXY, false);
@@ -301,6 +350,7 @@
     Matrix4 shadowMatrixZ;
     const float casterAlpha;
     const SkPath* casterPath;
+    const Rect localClipRect;
     const Vector3 lightCenter;
 };
 
@@ -374,7 +424,47 @@
  */
 struct EndLayerOp : RecordedOp {
     EndLayerOp()
-            : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), Rect(), nullptr) {}
+            : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {}
+};
+
+struct BeginUnclippedLayerOp : RecordedOp {
+    BeginUnclippedLayerOp(BASE_PARAMS)
+            : SUPER(BeginUnclippedLayerOp) {}
+};
+
+struct EndUnclippedLayerOp : RecordedOp {
+    EndUnclippedLayerOp()
+            : RecordedOp(RecordedOpId::EndUnclippedLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {}
+};
+
+struct CopyToLayerOp : RecordedOp {
+    CopyToLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle)
+            : RecordedOp(RecordedOpId::CopyToLayerOp,
+                    op.unmappedBounds,
+                    op.localMatrix,
+                    nullptr, // clip intentionally ignored
+                    op.paint)
+            , layerHandle(layerHandle) {}
+
+    // Records a handle to the Layer object, since the Layer itself won't be
+    // constructed until after this operation is constructed.
+    OffscreenBuffer** layerHandle;
+};
+
+
+// draw the parameter layer underneath
+struct CopyFromLayerOp : RecordedOp {
+    CopyFromLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle)
+            : RecordedOp(RecordedOpId::CopyFromLayerOp,
+                    op.unmappedBounds,
+                    op.localMatrix,
+                    nullptr, // clip intentionally ignored
+                    op.paint)
+            , layerHandle(layerHandle) {}
+
+    // Records a handle to the Layer object, since the Layer itself won't be
+    // constructed until after this operation is constructed.
+    OffscreenBuffer** layerHandle;
 };
 
 /**
@@ -388,18 +478,18 @@
     LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
             : SUPER_PAINTLESS(LayerOp)
             , layerHandle(layerHandle)
-            , alpha(paint->getAlpha() / 255.0f)
+            , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
             , mode(PaintUtils::getXfermodeDirect(paint))
-            , colorFilter(paint->getColorFilter())
+            , colorFilter(paint ? paint->getColorFilter() : nullptr)
             , destroy(true) {}
 
     LayerOp(RenderNode& node)
-        : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), Rect(node.getWidth(), node.getHeight()), nullptr)
-        , layerHandle(node.getLayerHandle())
-        , alpha(node.properties().layerProperties().alpha() / 255.0f)
-        , mode(node.properties().layerProperties().xferMode())
-        , colorFilter(node.properties().layerProperties().colorFilter())
-        , destroy(false) {}
+            : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr)
+            , layerHandle(node.getLayerHandle())
+            , alpha(node.properties().layerProperties().alpha() / 255.0f)
+            , mode(node.properties().layerProperties().xferMode())
+            , colorFilter(node.properties().layerProperties().colorFilter())
+            , destroy(false) {}
 
     // Records a handle to the Layer object, since the Layer itself won't be
     // constructed until after this operation is constructed.
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f75d8d4..78855e5 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -39,14 +39,14 @@
             "prepareDirty called a second time during a recording!");
     mDisplayList = new DisplayList();
 
-    mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3());
+    mState.initializeRecordingSaveStack(width, height);
 
     mDeferredBarrierType = DeferredBarrierType::InOrder;
     mState.setDirtyClip(false);
-    mRestoreSaveCount = -1;
 }
 
 DisplayList* RecordingCanvas::finishRecording() {
+    restoreToCount(1);
     mPaintMap.clear();
     mRegionMap.clear();
     mPathMap.clear();
@@ -83,6 +83,8 @@
 void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
     if (removed.flags & Snapshot::kFlagIsFboLayer) {
         addOp(new (alloc()) EndLayerOp());
+    } else if (removed.flags & Snapshot::kFlagIsLayer) {
+        addOp(new (alloc()) EndUnclippedLayerOp());
     }
 }
 
@@ -95,28 +97,18 @@
 }
 
 void RecordingCanvas::RecordingCanvas::restore() {
-    if (mRestoreSaveCount < 0) {
-        restoreToCount(getSaveCount() - 1);
-        return;
-    }
-
-    mRestoreSaveCount--;
     mState.restore();
 }
 
 void RecordingCanvas::restoreToCount(int saveCount) {
-    mRestoreSaveCount = saveCount;
     mState.restoreToCount(saveCount);
 }
 
-int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
-        SkCanvas::SaveFlags flags) {
-    if (!(flags & SkCanvas::kClipToLayer_SaveFlag)) {
-        LOG_ALWAYS_FATAL("unclipped layers not supported");
-    }
+int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
+        const SkPaint* paint, SkCanvas::SaveFlags flags) {
     // force matrix/clip isolation for layer
     flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
-
+    bool clippedLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
 
     const Snapshot& previous = *mState.currentSnapshot();
 
@@ -124,51 +116,70 @@
     // operations will be able to store and restore the current clip and transform info, and
     // quick rejection will be correct (for display lists)
 
-    const Rect untransformedBounds(left, top, right, bottom);
+    const Rect unmappedBounds(left, top, right, bottom);
 
     // determine clipped bounds relative to previous viewport.
-    Rect visibleBounds = untransformedBounds;
+    Rect visibleBounds = unmappedBounds;
     previous.transform->mapRect(visibleBounds);
 
+    if (CC_UNLIKELY(!clippedLayer
+            && previous.transform->rectToRect()
+            && visibleBounds.contains(previous.getRenderTargetClip()))) {
+        // unlikely case where an unclipped savelayer is recorded with a clip it can use,
+        // as none of its unaffected/unclipped area is visible
+        clippedLayer = true;
+        flags |= SkCanvas::kClipToLayer_SaveFlag;
+    }
 
     visibleBounds.doIntersect(previous.getRenderTargetClip());
     visibleBounds.snapToPixelBoundaries();
-
-    Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
-    visibleBounds.doIntersect(previousViewport);
+    visibleBounds.doIntersect(Rect(previous.getViewportWidth(), previous.getViewportHeight()));
 
     // Map visible bounds back to layer space, and intersect with parameter bounds
     Rect layerBounds = visibleBounds;
     Matrix4 inverse;
     inverse.loadInverse(*previous.transform);
     inverse.mapRect(layerBounds);
-    layerBounds.doIntersect(untransformedBounds);
+    layerBounds.doIntersect(unmappedBounds);
 
     int saveValue = mState.save((int) flags);
     Snapshot& snapshot = *mState.writableSnapshot();
 
-    // layerBounds is now original bounds, but with clipped to clip
-    // and viewport to ensure it's minimal size.
-    if (layerBounds.isEmpty() || untransformedBounds.isEmpty()) {
+    // layerBounds is in original bounds space, but clipped by current recording clip
+    if (layerBounds.isEmpty() || unmappedBounds.isEmpty()) {
         // Don't bother recording layer, since it's been rejected
-        snapshot.resetClip(0, 0, 0, 0);
+        if (CC_LIKELY(clippedLayer)) {
+            snapshot.resetClip(0, 0, 0, 0);
+        }
         return saveValue;
     }
 
-    snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
-    snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight());
-    snapshot.transform->loadTranslate(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
+    if (CC_LIKELY(clippedLayer)) {
+        auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed
 
-    Rect clip = layerBounds;
-    clip.translate(-untransformedBounds.left, -untransformedBounds.top);
-    snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
-    snapshot.roundRectClipState = nullptr;
+        snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
+        snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
+        snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
 
-    addOp(new (alloc()) BeginLayerOp(
-            Rect(left, top, right, bottom),
-            *previous.transform, // transform to *draw* with
-            previous.getRenderTargetClip(), // clip to *draw* with
-            refPaint(paint)));
+        Rect clip = layerBounds;
+        clip.translate(-unmappedBounds.left, -unmappedBounds.top);
+        snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
+        snapshot.roundRectClipState = nullptr;
+
+        addOp(new (alloc()) BeginLayerOp(
+                unmappedBounds,
+                *previous.transform, // transform to *draw* with
+                previousClip, // clip to *draw* with
+                refPaint(paint)));
+    } else {
+        snapshot.flags |= Snapshot::kFlagIsLayer;
+
+        addOp(new (alloc()) BeginUnclippedLayerOp(
+                unmappedBounds,
+                *mState.currentSnapshot()->transform,
+                getRecordedClip(),
+                refPaint(paint)));
+    }
 
     return saveValue;
 }
@@ -229,11 +240,10 @@
 }
 
 void RecordingCanvas::drawPaint(const SkPaint& paint) {
-    // TODO: more efficient recording?
     addOp(new (alloc()) RectOp(
-            mState.getRenderTargetClipBounds(),
+            mState.getRenderTargetClipBounds(), // OK, since we've not passed transform
             Matrix4::identity(),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint)));
 }
 
@@ -253,7 +263,7 @@
     addOp(new (alloc()) PointsOp(
             calcBoundsOfPoints(points, floatCount),
             *mState.currentSnapshot()->transform,
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
 }
 
@@ -264,7 +274,7 @@
     addOp(new (alloc()) LinesOp(
             calcBoundsOfPoints(points, floatCount),
             *mState.currentSnapshot()->transform,
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
 }
 
@@ -272,7 +282,7 @@
     addOp(new (alloc()) RectOp(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint)));
 }
 
@@ -305,7 +315,7 @@
     addOp(new (alloc()) SimpleRectsOp(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(paint), rectData, vertexCount));
 }
 
@@ -339,7 +349,7 @@
     addOp(new (alloc()) RoundRectOp(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint), rx, ry));
 }
 
@@ -358,7 +368,7 @@
     refBitmapsInShader(paint->value.getShader());
     addOp(new (alloc()) RoundRectPropsOp(
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             &paint->value,
             &left->value, &top->value, &right->value, &bottom->value,
             &rx->value, &ry->value));
@@ -380,7 +390,7 @@
     refBitmapsInShader(paint->value.getShader());
     addOp(new (alloc()) CirclePropsOp(
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             &paint->value,
             &x->value, &y->value, &radius->value));
 }
@@ -390,7 +400,7 @@
     addOp(new (alloc()) OvalOp(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint)));
 }
 
@@ -399,7 +409,7 @@
     addOp(new (alloc()) ArcOp(
             Rect(left, top, right, bottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint),
             startAngle, sweepAngle, useCenter));
 }
@@ -408,7 +418,7 @@
     addOp(new (alloc()) PathOp(
             Rect(path.getBounds()),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint), refPath(&path)));
 }
 
@@ -459,7 +469,7 @@
         addOp(new (alloc()) BitmapRectOp(
                 Rect(dstLeft, dstTop, dstRight, dstBottom),
                 *(mState.currentSnapshot()->transform),
-                mState.getRenderTargetClipBounds(),
+                getRecordedClip(),
                 refPaint(paint), refBitmap(bitmap),
                 Rect(srcLeft, srcTop, srcRight, srcBottom)));
     }
@@ -471,7 +481,7 @@
     addOp(new (alloc()) BitmapMeshOp(
             calcBoundsOfPoints(vertices, vertexCount * 2),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
             refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
             refBuffer<int>(colors, vertexCount))); // 1 color per vertex
@@ -483,7 +493,7 @@
     addOp(new (alloc()) PatchOp(
             Rect(dstLeft, dstTop, dstRight, dstBottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
 }
 
@@ -499,7 +509,7 @@
     addOp(new (alloc()) TextOp(
             Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint), glyphs, positions, glyphCount, x, y));
     drawTextDecorations(x, y, totalAdvance, paint);
 }
@@ -509,9 +519,9 @@
     if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
     glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
     addOp(new (alloc()) TextOnPathOp(
-            mState.getRenderTargetClipBounds(), // TODO: explicitly define bounds
+            mState.getLocalClipBounds(), // TODO: explicitly define bounds
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
 }
 
@@ -519,7 +529,7 @@
     addOp(new (alloc()) BitmapOp(
             Rect(bitmap->width(), bitmap->height()),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             refPaint(paint), refBitmap(*bitmap)));
 }
 
@@ -528,7 +538,7 @@
     RenderNodeOp* op = new (alloc()) RenderNodeOp(
             Rect(stagingProps.getWidth(), stagingProps.getHeight()),
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             renderNode);
     int opIndex = addOp(op);
     int childIndex = mDisplayList->addChild(op);
@@ -554,16 +564,16 @@
     addOp(new (alloc()) TextureLayerOp(
             Rect(layer->getWidth(), layer->getHeight()),
             totalTransform,
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             layer));
 }
 
 void RecordingCanvas::callDrawGLFunction(Functor* functor) {
     mDisplayList->functors.push_back(functor);
     addOp(new (alloc()) FunctorOp(
-            mState.getRenderTargetClipBounds(), // TODO: explicitly define bounds
+            mState.getLocalClipBounds(), // TODO: explicitly define bounds
             *(mState.currentSnapshot()->transform),
-            mState.getRenderTargetClipBounds(),
+            getRecordedClip(),
             functor));
 }
 
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 470f9ec..8aa7506 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -36,6 +36,7 @@
 namespace android {
 namespace uirenderer {
 
+struct ClipBase;
 class DeferredLayerUpdater;
 struct RecordedOp;
 
@@ -199,6 +200,9 @@
     virtual bool drawTextAbsolutePos() const override { return false; }
 
 private:
+    const ClipBase* getRecordedClip() {
+        return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
+    }
 
     void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
     void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
@@ -310,7 +314,6 @@
     DisplayList* mDisplayList = nullptr;
     bool mHighContrastText = false;
     SkAutoTUnref<SkDrawFilter> mDrawFilter;
-    int mRestoreSaveCount = -1;
 }; // class RecordingCanvas
 
 }; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index b6f50b1..8e4a3df 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -47,11 +47,11 @@
 class DisplayListCanvas;
 class DisplayListOp;
 class OpenGLRenderer;
-class OpReorderer;
 class Rect;
 class SkiaShader;
 
 #if HWUI_NEW_OPS
+class FrameBuilder;
 class OffscreenBuffer;
 struct RenderNodeOp;
 typedef OffscreenBuffer layer_t;
@@ -87,7 +87,7 @@
  */
 class RenderNode : public VirtualLightRefBase {
 friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
-friend class OpReorderer;
+friend class FrameBuilder;
 public:
     enum DirtyPropertyMask {
         GENERIC         = 1 << 1,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 130cc80..976f775 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -162,15 +162,15 @@
     mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
 }
 
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
-        const SkPaint* paint, SaveFlags flags) {
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
     SkRect rect;
-    if (rectPtr) {
-        rect = *rectPtr;
-    } else if(!mCanvas->getClipBounds(&rect)) {
+    if (saveLayerRec.fBounds) {
+        rect = *saveLayerRec.fBounds;
+    } else if (!mCanvas->getClipBounds(&rect)) {
         rect = SkRect::MakeEmpty();
     }
-    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+    mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
+                       (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags));
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index 0089fb5..e342d19 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -47,7 +47,7 @@
     virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
 
     virtual void willSave() override;
-    virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+    virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
     virtual void willRestore() override;
 
     virtual void didConcat(const SkMatrix&) override;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 83652c6..6f4a683 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -57,7 +57,7 @@
 }
 
 static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
-    caches->textureState().bindTexture(texture->id);
+    caches->textureState().bindTexture(texture->id());
     texture->setWrapST(wrapS, wrapT);
 }
 
@@ -219,8 +219,8 @@
 
     outData->bitmapSampler = (*textureUnit)++;
 
-    const float width = outData->bitmapTexture->width;
-    const float height = outData->bitmapTexture->height;
+    const float width = outData->bitmapTexture->width();
+    const float height = outData->bitmapTexture->height();
 
     description->hasBitmap = true;
     if (!caches.extensions().hasNPot()
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 194aa57..dbaa905 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -116,7 +116,7 @@
          * Indicates that this snapshot or an ancestor snapshot is
          * an FBO layer.
          */
-        kFlagFboTarget = 0x8,
+        kFlagFboTarget = 0x8, // TODO: remove for HWUI_NEW_OPS
     };
 
     /**
@@ -167,6 +167,7 @@
     const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
     bool clipIsSimple() const { return mClipArea->isSimple(); }
     const ClipArea& getClipArea() const { return *mClipArea; }
+    ClipArea& mutateClipArea() { return *mClipArea; }
 
     /**
      * Resets the clip to the specified rect.
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 51f1652..f1e28b7 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -187,13 +187,10 @@
         texture = new ShadowTexture(caches);
         texture->left = shadow.penX;
         texture->top = shadow.penY;
-        texture->width = shadow.width;
-        texture->height = shadow.height;
         texture->generation = 0;
         texture->blend = true;
 
         const uint32_t size = shadow.width * shadow.height;
-        texture->bitmapSize = size;
 
         // Don't even try to cache a bitmap that's bigger than the cache
         if (size < mMaxSize) {
@@ -202,15 +199,11 @@
             }
         }
 
-        glGenTextures(1, &texture->id);
-
-        caches.textureState().bindTexture(texture->id);
         // Textures are Alpha8
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+        texture->upload(GL_ALPHA, shadow.width, shadow.height,
                 GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
-
         texture->setFilter(GL_LINEAR);
         texture->setWrap(GL_CLAMP_TO_EDGE);
 
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 5195b45..8a6b28d 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -14,14 +14,29 @@
  * limitations under the License.
  */
 
-#include <utils/Log.h>
-
 #include "Caches.h"
 #include "Texture.h"
+#include "utils/TraceUtils.h"
+
+#include <utils/Log.h>
+
+#include <SkCanvas.h>
 
 namespace android {
 namespace uirenderer {
 
+static int bytesPerPixel(GLint glFormat) {
+    switch (glFormat) {
+    case GL_ALPHA:
+        return 1;
+    case GL_RGB:
+        return 3;
+    case GL_RGBA:
+    default:
+        return 4;
+    }
+}
+
 void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
         GLenum renderTarget) {
 
@@ -32,7 +47,7 @@
         mWrapT = wrapT;
 
         if (bindTexture) {
-            mCaches.textureState().bindTexture(renderTarget, id);
+            mCaches.textureState().bindTexture(renderTarget, mId);
         }
 
         glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
@@ -50,7 +65,7 @@
         mMagFilter = mag;
 
         if (bindTexture) {
-            mCaches.textureState().bindTexture(renderTarget, id);
+            mCaches.textureState().bindTexture(renderTarget, mId);
         }
 
         if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
@@ -60,8 +75,189 @@
     }
 }
 
-void Texture::deleteTexture() const {
-    mCaches.textureState().deleteTexture(id);
+void Texture::deleteTexture() {
+    mCaches.textureState().deleteTexture(mId);
+    mId = 0;
+}
+
+bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) {
+    if (mWidth == width && mHeight == height && mFormat == format) {
+        return false;
+    }
+    mWidth = width;
+    mHeight = height;
+    mFormat = format;
+    notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat));
+    return true;
+}
+
+void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
+        GLenum format, GLenum type, const void* pixels) {
+    bool needsAlloc = updateSize(width, height, internalformat);
+    if (!needsAlloc && !pixels) {
+        return;
+    }
+    mCaches.textureState().activateTexture(0);
+    if (!mId) {
+        glGenTextures(1, &mId);
+        needsAlloc = true;
+    }
+    mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
+    if (needsAlloc) {
+        glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+                format, type, pixels);
+    } else {
+        glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+                format, type, pixels);
+    }
+}
+
+static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
+        GLsizei width, GLsizei height, const GLvoid * data) {
+
+    glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
+    const bool useStride = stride != width
+            && Caches::getInstance().extensions().hasUnpackRowLength();
+    if ((stride == width) || useStride) {
+        if (useStride) {
+            glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+        }
+
+        if (resize) {
+            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+        } else {
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+        }
+
+        if (useStride) {
+            glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+        }
+    } else {
+        //  With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+        //  if the stride doesn't match the width
+
+        GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
+        if (!temp) return;
+
+        uint8_t * pDst = (uint8_t *)temp;
+        uint8_t * pSrc = (uint8_t *)data;
+        for (GLsizei i = 0; i < height; i++) {
+            memcpy(pDst, pSrc, width * bpp);
+            pDst += width * bpp;
+            pSrc += stride * bpp;
+        }
+
+        if (resize) {
+            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+        } else {
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+        }
+
+        free(temp);
+    }
+}
+
+static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
+        bool resize, GLenum format, GLenum type) {
+    uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
+            bitmap.width(), bitmap.height(), bitmap.getPixels());
+}
+
+static void colorTypeToGlFormatAndType(SkColorType colorType,
+        GLint* outFormat, GLint* outType) {
+    switch (colorType) {
+    case kAlpha_8_SkColorType:
+        *outFormat = GL_ALPHA;
+        *outType = GL_UNSIGNED_BYTE;
+        break;
+    case kRGB_565_SkColorType:
+        *outFormat = GL_RGB;
+        *outType = GL_UNSIGNED_SHORT_5_6_5;
+        break;
+    // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
+    case kARGB_4444_SkColorType:
+    case kIndex_8_SkColorType:
+    case kN32_SkColorType:
+        *outFormat = GL_RGBA;
+        *outType = GL_UNSIGNED_BYTE;
+        break;
+    default:
+        LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
+        break;
+    }
+}
+
+void Texture::upload(const SkBitmap& bitmap) {
+    SkAutoLockPixels alp(bitmap);
+
+    if (!bitmap.readyToDraw()) {
+        ALOGE("Cannot generate texture from bitmap");
+        return;
+    }
+
+    ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
+
+    // We could also enable mipmapping if both bitmap dimensions are powers
+    // of 2 but we'd have to deal with size changes. Let's keep this simple
+    const bool canMipMap = mCaches.extensions().hasNPot();
+
+    // If the texture had mipmap enabled but not anymore,
+    // force a glTexImage2D to discard the mipmap levels
+    bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
+
+    if (!mId) {
+        glGenTextures(1, &mId);
+        needsAlloc = true;
+    }
+
+    GLint format, type;
+    colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type);
+
+    if (updateSize(bitmap.width(), bitmap.height(), format)) {
+        needsAlloc = true;
+    }
+
+    blend = !bitmap.isOpaque();
+    mCaches.textureState().bindTexture(mId);
+
+    if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
+            || bitmap.colorType() == kIndex_8_SkColorType)) {
+        SkBitmap rgbaBitmap;
+        rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight,
+                bitmap.alphaType()));
+        rgbaBitmap.eraseColor(0);
+
+        SkCanvas canvas(rgbaBitmap);
+        canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+
+        uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type);
+    } else {
+        uploadSkBitmapToTexture(bitmap, needsAlloc, format, type);
+    }
+
+    if (canMipMap) {
+        mipMap = bitmap.hasHardwareMipMap();
+        if (mipMap) {
+            glGenerateMipmap(GL_TEXTURE_2D);
+        }
+    }
+
+    if (mFirstFilter) {
+        setFilter(GL_NEAREST);
+    }
+
+    if (mFirstWrap) {
+        setWrap(GL_CLAMP_TO_EDGE);
+    }
+}
+
+void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) {
+    mId = id;
+    mWidth = width;
+    mHeight = height;
+    mFormat = format;
+    // We're wrapping an existing texture, so don't double count this memory
+    notifySizeChanged(0);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 1c544b9..4e8e6dc 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -17,20 +17,27 @@
 #ifndef ANDROID_HWUI_TEXTURE_H
 #define ANDROID_HWUI_TEXTURE_H
 
+#include "GpuMemoryTracker.h"
+
 #include <GLES2/gl2.h>
+#include <SkBitmap.h>
 
 namespace android {
 namespace uirenderer {
 
 class Caches;
 class UvMapper;
+class Layer;
 
 /**
  * Represents an OpenGL texture.
  */
-class Texture {
+class Texture : public GpuMemoryTracker {
 public:
-    Texture(Caches& caches) : mCaches(caches) { }
+    Texture(Caches& caches)
+        : GpuMemoryTracker(GpuObjectType::Texture)
+        , mCaches(caches)
+    { }
 
     virtual ~Texture() { }
 
@@ -53,12 +60,55 @@
     /**
      * Convenience method to call glDeleteTextures() on this texture's id.
      */
-    void deleteTexture() const;
+    void deleteTexture();
 
     /**
-     * Name of the texture.
+     * Sets the width, height, and format of the texture along with allocating
+     * the texture ID. Does nothing if the width, height, and format are already
+     * the requested values.
+     *
+     * The image data is undefined after calling this.
      */
-    GLuint id = 0;
+    void resize(uint32_t width, uint32_t height, GLint format) {
+        upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr);
+    }
+
+    /**
+     * Updates this Texture with the contents of the provided SkBitmap,
+     * also setting the appropriate width, height, and format. It is not necessary
+     * to call resize() prior to this.
+     *
+     * Note this does not set the generation from the SkBitmap.
+     */
+    void upload(const SkBitmap& source);
+
+    /**
+     * Basically glTexImage2D/glTexSubImage2D.
+     */
+    void upload(GLint internalformat, uint32_t width, uint32_t height,
+            GLenum format, GLenum type, const void* pixels);
+
+    /**
+     * Wraps an existing texture.
+     */
+    void wrap(GLuint id, uint32_t width, uint32_t height, GLint format);
+
+    GLuint id() const {
+        return mId;
+    }
+
+    uint32_t width() const {
+        return mWidth;
+    }
+
+    uint32_t height() const {
+        return mHeight;
+    }
+
+    GLint format() const {
+        return mFormat;
+    }
+
     /**
      * Generation of the backing bitmap,
      */
@@ -68,14 +118,6 @@
      */
     bool blend = false;
     /**
-     * Width of the backing bitmap.
-     */
-    uint32_t width = 0;
-    /**
-     * Height of the backing bitmap.
-     */
-    uint32_t height = 0;
-    /**
      * Indicates whether this texture should be cleaned up after use.
      */
     bool cleanup = false;
@@ -100,6 +142,19 @@
     void* isInUse = nullptr;
 
 private:
+    // TODO: Temporarily grant private access to Layer, remove once
+    // Layer can be de-tangled from being a dual-purpose render target
+    // and external texture wrapper
+    friend class Layer;
+
+    // Returns true if the size changed, false if it was the same
+    bool updateSize(uint32_t width, uint32_t height, GLint format);
+
+    GLuint mId = 0;
+    uint32_t mWidth = 0;
+    uint32_t mHeight = 0;
+    GLint mFormat = 0;
+
     /**
      * Last wrap modes set on this texture.
      */
@@ -120,7 +175,7 @@
 
 class AutoTexture {
 public:
-    AutoTexture(const Texture* texture)
+    AutoTexture(Texture* texture)
             : texture(texture) {}
     ~AutoTexture() {
         if (texture && texture->cleanup) {
@@ -129,7 +184,7 @@
         }
     }
 
-    const Texture *const texture;
+    Texture* const texture;
 }; // class AutoTexture
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 21901cf..31bfa3a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -166,7 +166,8 @@
         if (canCache) {
             texture = new Texture(Caches::getInstance());
             texture->bitmapSize = size;
-            Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
+            texture->generation = bitmap->getGenerationID();
+            texture->upload(*bitmap);
 
             mSize += size;
             TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
@@ -179,7 +180,8 @@
     } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
         // Texture was in the cache but is dirty, re-upload
         // TODO: Re-adjust the cache size if the bitmap's dimensions have changed
-        Caches::getInstance().textureState().generateTexture(bitmap, texture, true);
+        texture->upload(*bitmap);
+        texture->generation = bitmap->getGenerationID();
     }
 
     return texture;
@@ -204,7 +206,8 @@
         const uint32_t size = bitmap->rowBytes() * bitmap->height();
         texture = new Texture(Caches::getInstance());
         texture->bitmapSize = size;
-        Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
+        texture->upload(*bitmap);
+        texture->generation = bitmap->getGenerationID();
         texture->cleanup = true;
     }
 
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 191c8a8..463450c 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -25,6 +25,7 @@
 #include "Debug.h"
 
 #include <vector>
+#include <unordered_map>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 56cb104..793df92 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -407,8 +407,9 @@
     float canvasScaleY = 1.0f;
     if (mCanvasMatrix.getSkewX() == 0 && mCanvasMatrix.getSkewY() == 0) {
         // Only use the scale value when there's no skew or rotation in the canvas matrix.
-        canvasScaleX = mCanvasMatrix.getScaleX();
-        canvasScaleY = mCanvasMatrix.getScaleY();
+        // TODO: Add a cts test for drawing VD on a canvas with negative scaling factors.
+        canvasScaleX = fabs(mCanvasMatrix.getScaleX());
+        canvasScaleY = fabs(mCanvasMatrix.getScaleY());
     }
     int scaledWidth = (int) (mBounds.width() * canvasScaleX);
     int scaledHeight = (int) (mBounds.height() * canvasScaleY);
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index d2685da..8ba4761 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -111,11 +111,11 @@
 
 CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount)
         : mTexture(Caches::getInstance())
+        , mWidth(width)
+        , mHeight(height)
         , mFormat(format)
         , mMaxQuadCount(maxQuadCount)
         , mCaches(Caches::getInstance()) {
-    mTexture.width = width;
-    mTexture.height = height;
     mTexture.blend = true;
 
     mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
@@ -160,10 +160,7 @@
         delete mPixelBuffer;
         mPixelBuffer = nullptr;
     }
-    if (mTexture.id) {
-        mCaches.textureState().deleteTexture(mTexture.id);
-        mTexture.id = 0;
-    }
+    mTexture.deleteTexture();
     mDirty = false;
     mCurrentQuad = 0;
 }
@@ -183,22 +180,9 @@
         mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight());
     }
 
-    if (!mTexture.id) {
-        glGenTextures(1, &mTexture.id);
-
-        mCaches.textureState().bindTexture(mTexture.id);
-        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-        // Initialize texture dimensions
-        glTexImage2D(GL_TEXTURE_2D, 0, mFormat, getWidth(), getHeight(), 0,
-                mFormat, GL_UNSIGNED_BYTE, nullptr);
-
-        const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    }
+    mTexture.resize(mWidth, mHeight, mFormat);
+    mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST);
+    mTexture.setWrap(GL_CLAMP_TO_EDGE);
 }
 
 bool CacheTexture::upload() {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 6dabc76..5510666 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -92,11 +92,11 @@
     bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
 
     inline uint16_t getWidth() const {
-        return mTexture.width;
+        return mWidth;
     }
 
     inline uint16_t getHeight() const {
-        return mTexture.height;
+        return mHeight;
     }
 
     inline GLenum getFormat() const {
@@ -122,7 +122,7 @@
 
     GLuint getTextureId() {
         allocatePixelBuffer();
-        return mTexture.id;
+        return mTexture.id();
     }
 
     inline bool isDirty() const {
@@ -183,6 +183,7 @@
 
     PixelBuffer* mPixelBuffer = nullptr;
     Texture mTexture;
+    uint32_t mWidth, mHeight;
     GLenum mFormat;
     bool mLinearFiltering = false;
     bool mDirty = false;
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index 6b44557..98c94df 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -34,24 +34,23 @@
 
 OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
         uint32_t viewportWidth, uint32_t viewportHeight)
-        : renderState(renderState)
+        : GpuMemoryTracker(GpuObjectType::OffscreenBuffer)
+        , renderState(renderState)
         , viewportWidth(viewportWidth)
         , viewportHeight(viewportHeight)
         , texture(caches) {
-    texture.width = computeIdealDimension(viewportWidth);
-    texture.height = computeIdealDimension(viewportHeight);
+    uint32_t width = computeIdealDimension(viewportWidth);
+    uint32_t height = computeIdealDimension(viewportHeight);
+    texture.resize(width, height, GL_RGBA);
     texture.blend = true;
-
-    caches.textureState().activateTexture(0);
-    glGenTextures(1, &texture.id);
-    caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
-
-    texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
+    texture.setWrap(GL_CLAMP_TO_EDGE);
     // not setting filter on texture, since it's set when drawing, based on transform
+}
 
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
-            GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+Rect OffscreenBuffer::getTextureCoordinates() {
+    const float texX = 1.0f / static_cast<float>(texture.width());
+    const float texY = 1.0f / static_cast<float>(texture.height());
+    return Rect(0, viewportHeight * texY, viewportWidth * texX, 0);
 }
 
 void OffscreenBuffer::updateMeshFromRegion() {
@@ -63,8 +62,8 @@
     size_t count;
     const android::Rect* rects = safeRegion.getArray(&count);
 
-    const float texX = 1.0f / float(texture.width);
-    const float texY = 1.0f / float(texture.height);
+    const float texX = 1.0f / float(texture.width());
+    const float texY = 1.0f / float(texture.height());
 
     FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
     TextureVertex* mesh = &meshVector[0];
@@ -151,8 +150,8 @@
 OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
         const uint32_t width, const uint32_t height) {
     RenderState& renderState = layer->renderState;
-    if (layer->texture.width == OffscreenBuffer::computeIdealDimension(width)
-            && layer->texture.height == OffscreenBuffer::computeIdealDimension(height)) {
+    if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width)
+            && layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) {
         // resize in place
         layer->viewportWidth = width;
         layer->viewportHeight = height;
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index fac6c35..94155ef 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -17,10 +17,10 @@
 #ifndef ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
 #define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
 
+#include <GpuMemoryTracker.h>
 #include "Caches.h"
 #include "Texture.h"
 #include "utils/Macros.h"
-
 #include <ui/Region.h>
 
 #include <set>
@@ -40,12 +40,14 @@
  * viewport bounds, since textures are always allocated with width / height as a multiple of 64, for
  * the purpose of improving reuse.
  */
-class OffscreenBuffer {
+class OffscreenBuffer : GpuMemoryTracker {
 public:
     OffscreenBuffer(RenderState& renderState, Caches& caches,
             uint32_t viewportWidth, uint32_t viewportHeight);
     ~OffscreenBuffer();
 
+    Rect getTextureCoordinates();
+
     // must be called prior to rendering, to construct/update vertex buffer
     void updateMeshFromRegion();
 
@@ -56,7 +58,7 @@
 
     static uint32_t computeIdealDimension(uint32_t dimension);
 
-    uint32_t getSizeInBytes() { return texture.width * texture.height * 4; }
+    uint32_t getSizeInBytes() { return texture.objectSize(); }
 
     RenderState& renderState;
 
@@ -122,8 +124,8 @@
 
         Entry(OffscreenBuffer* layer)
                 : layer(layer)
-                , width(layer->texture.width)
-                , height(layer->texture.height) {
+                , width(layer->texture.width())
+                , height(layer->texture.height()) {
         }
 
         static int compare(const Entry& lhs, const Entry& rhs);
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 4fa8200..b6dba02 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <GpuMemoryTracker.h>
 #include "renderstate/RenderState.h"
 
 #include "renderthread/CanvasContext.h"
 #include "renderthread/EglManager.h"
 #include "utils/GLUtils.h"
-
 #include <algorithm>
 
 namespace android {
@@ -40,6 +40,8 @@
 void RenderState::onGLContextCreated() {
     LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
             "State object lifecycle not managed correctly");
+    GpuMemoryTracker::onGLContextCreated();
+
     mBlend = new Blend();
     mMeshState = new MeshState();
     mScissor = new Scissor();
@@ -106,6 +108,8 @@
     mScissor = nullptr;
     delete mStencil;
     mStencil = nullptr;
+
+    GpuMemoryTracker::onGLContextDestroyed();
 }
 
 void RenderState::flush(Caches::FlushMode mode) {
@@ -205,17 +209,6 @@
     }
 }
 
-void RenderState::requireGLContext() {
-    assertOnGLThread();
-    LOG_ALWAYS_FATAL_IF(!mRenderThread.eglManager().hasEglContext(),
-            "No GL context!");
-}
-
-void RenderState::assertOnGLThread() {
-    pthread_t curr = pthread_self();
-    LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!");
-}
-
 class DecStrongTask : public renderthread::RenderTask {
 public:
     DecStrongTask(VirtualLightRefBase* object) : mObject(object) {}
@@ -231,7 +224,11 @@
 };
 
 void RenderState::postDecStrong(VirtualLightRefBase* object) {
-    mRenderThread.queue(new DecStrongTask(object));
+    if (pthread_equal(mThreadId, pthread_self())) {
+        object->decStrong(nullptr);
+    } else {
+        mRenderThread.queue(new DecStrongTask(object));
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -310,7 +307,7 @@
             texture.texture->setFilter(texture.filter, true, false, texture.target);
         }
 
-        mCaches->textureState().bindTexture(texture.target, texture.texture->id);
+        mCaches->textureState().bindTexture(texture.target, texture.texture->id());
         meshState().enableTexCoordsVertexArray();
         meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride);
 
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index dcd5ea6..e5d3e79 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -86,8 +86,6 @@
         mRegisteredContexts.erase(context);
     }
 
-    void requireGLContext();
-
     // TODO: This system is a little clunky feeling, this could use some
     // more thinking...
     void postDecStrong(VirtualLightRefBase* object);
@@ -107,7 +105,6 @@
 private:
     void interruptForFunctorInvoke();
     void resumeFromFunctorInvoke();
-    void assertOnGLThread();
 
     RenderState(renderthread::RenderThread& thread);
     ~RenderState();
diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp
index 95dcd18..61dd8c3 100644
--- a/libs/hwui/renderstate/Scissor.cpp
+++ b/libs/hwui/renderstate/Scissor.cpp
@@ -15,6 +15,8 @@
  */
 #include "renderstate/Scissor.h"
 
+#include "Rect.h"
+
 #include <utils/Log.h>
 
 namespace android {
@@ -71,6 +73,26 @@
     return false;
 }
 
+void Scissor::set(int viewportHeight, const Rect& clip) {
+    // transform to Y-flipped GL space, and prevent negatives
+    GLint x = std::max(0, (int)clip.left);
+    GLint y = std::max(0, viewportHeight - (int)clip.bottom);
+    GLint width = std::max(0, ((int)clip.right) - x);
+    GLint height = std::max(0, (viewportHeight - (int)clip.top) - y);
+
+    if (x != mScissorX
+            || y != mScissorY
+            || width != mScissorWidth
+            || height != mScissorHeight) {
+        glScissor(x, y, width, height);
+
+        mScissorX = x;
+        mScissorY = y;
+        mScissorWidth = width;
+        mScissorHeight = height;
+    }
+}
+
 void Scissor::reset() {
     mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
 }
diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h
index b37ec58..f302244 100644
--- a/libs/hwui/renderstate/Scissor.h
+++ b/libs/hwui/renderstate/Scissor.h
@@ -22,11 +22,14 @@
 namespace android {
 namespace uirenderer {
 
+class Rect;
+
 class Scissor {
     friend class RenderState;
 public:
     bool setEnabled(bool enabled);
     bool set(GLint x, GLint y, GLint width, GLint height);
+    void set(int viewportHeight, const Rect& clip);
     void reset();
     bool isEnabled() { return mEnabled; }
     void dump();
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
index 1f50f71..26ebdee 100644
--- a/libs/hwui/renderstate/TextureState.cpp
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -34,134 +34,6 @@
     GL_TEXTURE3
 };
 
-static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
-        GLsizei width, GLsizei height, const GLvoid * data) {
-
-    glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
-    const bool useStride = stride != width
-            && Caches::getInstance().extensions().hasUnpackRowLength();
-    if ((stride == width) || useStride) {
-        if (useStride) {
-            glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
-        }
-
-        if (resize) {
-            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
-        } else {
-            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
-        }
-
-        if (useStride) {
-            glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-        }
-    } else {
-        //  With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
-        //  if the stride doesn't match the width
-
-        GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
-        if (!temp) return;
-
-        uint8_t * pDst = (uint8_t *)temp;
-        uint8_t * pSrc = (uint8_t *)data;
-        for (GLsizei i = 0; i < height; i++) {
-            memcpy(pDst, pSrc, width * bpp);
-            pDst += width * bpp;
-            pSrc += stride * bpp;
-        }
-
-        if (resize) {
-            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
-        } else {
-            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
-        }
-
-        free(temp);
-    }
-}
-
-static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
-        bool resize, GLenum format, GLenum type) {
-    uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
-            bitmap.width(), bitmap.height(), bitmap.getPixels());
-}
-
-void TextureState::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
-    SkAutoLockPixels alp(*bitmap);
-
-    if (!bitmap->readyToDraw()) {
-        ALOGE("Cannot generate texture from bitmap");
-        return;
-    }
-
-    ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height());
-
-    // We could also enable mipmapping if both bitmap dimensions are powers
-    // of 2 but we'd have to deal with size changes. Let's keep this simple
-    const bool canMipMap = Caches::getInstance().extensions().hasNPot();
-
-    // If the texture had mipmap enabled but not anymore,
-    // force a glTexImage2D to discard the mipmap levels
-    const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
-            bitmap->height() != int(texture->height) ||
-            (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
-
-    if (!regenerate) {
-        glGenTextures(1, &texture->id);
-    }
-
-    texture->generation = bitmap->getGenerationID();
-    texture->width = bitmap->width();
-    texture->height = bitmap->height();
-
-    bindTexture(texture->id);
-
-    switch (bitmap->colorType()) {
-    case kAlpha_8_SkColorType:
-        uploadSkBitmapToTexture(*bitmap, resize, GL_ALPHA, GL_UNSIGNED_BYTE);
-        texture->blend = true;
-        break;
-    case kRGB_565_SkColorType:
-        uploadSkBitmapToTexture(*bitmap, resize, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
-        texture->blend = false;
-        break;
-    case kN32_SkColorType:
-        uploadSkBitmapToTexture(*bitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
-        // Do this after calling getPixels() to make sure Skia's deferred
-        // decoding happened
-        texture->blend = !bitmap->isOpaque();
-        break;
-    case kARGB_4444_SkColorType:
-    case kIndex_8_SkColorType: {
-        SkBitmap rgbaBitmap;
-        rgbaBitmap.allocPixels(SkImageInfo::MakeN32(texture->width, texture->height,
-                bitmap->alphaType()));
-        rgbaBitmap.eraseColor(0);
-
-        SkCanvas canvas(rgbaBitmap);
-        canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
-
-        uploadSkBitmapToTexture(rgbaBitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
-        texture->blend = !bitmap->isOpaque();
-        break;
-    }
-    default:
-        ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
-        break;
-    }
-
-    if (canMipMap) {
-        texture->mipMap = bitmap->hasHardwareMipMap();
-        if (texture->mipMap) {
-            glGenerateMipmap(GL_TEXTURE_2D);
-        }
-    }
-
-    if (!regenerate) {
-        texture->setFilter(GL_NEAREST);
-        texture->setWrap(GL_CLAMP_TO_EDGE);
-    }
-}
-
 TextureState::TextureState()
         : mTextureUnit(0) {
     glActiveTexture(kTextureUnits[0]);
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
index 3a2b85a..ec94d7e 100644
--- a/libs/hwui/renderstate/TextureState.h
+++ b/libs/hwui/renderstate/TextureState.h
@@ -76,13 +76,6 @@
      */
     void unbindTexture(GLuint texture);
 
-    /**
-     * Generates the texture from a bitmap into the specified texture structure.
-     *
-     * @param regenerate If true, the bitmap data is reuploaded into the texture, but
-     *        no new texture is generated.
-     */
-    void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate);
 private:
     // total number of texture units available for use
     static const int kTextureUnitsCount = 4;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1af8f80..dd48a83 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <GpuMemoryTracker.h>
 #include "CanvasContext.h"
 
 #include "AnimationContext.h"
@@ -31,7 +32,7 @@
 #include "utils/TimeUtils.h"
 
 #if HWUI_NEW_OPS
-#include "OpReorderer.h"
+#include "FrameBuilder.h"
 #endif
 
 #include <cutils/properties.h>
@@ -338,14 +339,13 @@
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
-    OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+    FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
             mRenderNodes, mLightCenter);
     mLayerUpdateQueue.clear();
     BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
             mOpaque, mLightInfo);
     // TODO: profiler().draw(mCanvas);
-    reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
-
+    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
     bool drew = renderer.didDraw();
 
 #else
@@ -498,6 +498,8 @@
 
     mJankTracker.addFrame(*mCurrentFrameInfo);
     mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
+
+    GpuMemoryTracker::onFrameCompleted();
 }
 
 // Called by choreographer to do an RT-driven animation
@@ -513,11 +515,11 @@
 void CanvasContext::prepareAndDraw(RenderNode* node) {
     ATRACE_CALL();
 
+    nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
     int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
     UiFrameInfoBuilder(frameInfo)
         .addFlag(FrameInfoFlags::RTAnimation)
-        .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
-                mRenderThread.timeLord().latestVsync());
+        .setVsync(vsync, vsync);
 
     TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
     prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
@@ -579,7 +581,7 @@
     node->setPropertyFieldsDirty(RenderNode::GENERIC);
 
 #if HWUI_NEW_OPS
-    LOG_ALWAYS_FATAL("unsupported");
+    // TODO: support buildLayer
 #else
     mCanvas->markLayersAsBuildLayers();
     mCanvas->flushLayerUpdates();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 43282c9..72c7e4e 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -450,6 +450,11 @@
     } else {
         fprintf(file, "\nNo caches instance.\n");
     }
+#if HWUI_NEW_OPS
+    fprintf(file, "\nPipeline=FrameBuilder\n");
+#else
+    fprintf(file, "\nPipeline=OpenGLRenderer\n");
+#endif
     fflush(file);
     return nullptr;
 }
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 624f3bd..d56693c 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,6 +19,10 @@
 #include "DeferredLayerUpdater.h"
 #include "LayerRenderer.h"
 
+#include <unistd.h>
+#include <signal.h>
+#include <setjmp.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -121,5 +125,41 @@
     canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
 }
 
+static void defaultCrashHandler() {
+    fprintf(stderr, "RenderThread crashed!");
+}
+
+static jmp_buf gErrJmpBuff;
+static std::function<void()> gCrashHandler = defaultCrashHandler;
+
+static void signalHandler(int sig) {
+    longjmp(gErrJmpBuff, 1);
+}
+
+void TestUtils::setRenderThreadCrashHandler(std::function<void()> crashHandler) {
+    gCrashHandler = crashHandler;
+}
+
+void TestUtils::TestTask::run() {
+    struct sigaction act;
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = signalHandler;
+
+    if (setjmp(gErrJmpBuff)) {
+        gCrashHandler();
+        return;
+    }
+
+    sigaction(SIGABRT, &act, nullptr);
+
+
+    // RenderState only valid once RenderThread is running, so queried here
+    RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
+
+    renderState.onGLContextCreated();
+    rtCallback(renderthread::RenderThread::getInstance());
+    renderState.onGLContextDestroyed();
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ac14fc8..ae08142 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -53,6 +53,13 @@
             && MathUtils::areEqual(a.right, b.right) \
             && MathUtils::areEqual(a.bottom, b.bottom));
 
+#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \
+        EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \
+        if ((clipStatePtr)->mode == ClipMode::Rectangle) { \
+            EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \
+        } else { \
+            ADD_FAILURE() << "ClipState not a rect"; \
+        }
 /**
  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
  * (for e.g. accessing its RenderState)
@@ -164,21 +171,23 @@
         syncHierarchyPropertiesAndDisplayListImpl(node.get());
     }
 
+    static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
+        TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+        std::vector<sp<RenderNode>> vec;
+        vec.emplace_back(node);
+        return vec;
+    }
+
     typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
 
+    static void setRenderThreadCrashHandler(std::function<void()> crashHandler);
+
     class TestTask : public renderthread::RenderTask {
     public:
         TestTask(RtCallback rtCallback)
                 : rtCallback(rtCallback) {}
         virtual ~TestTask() {}
-        virtual void run() override {
-            // RenderState only valid once RenderThread is running, so queried here
-            RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
-
-            renderState.onGLContextCreated();
-            rtCallback(renderthread::RenderThread::getInstance());
-            renderState.onGLContextDestroyed();
-        };
+        virtual void run() override;
         RtCallback rtCallback;
     };
 
@@ -190,6 +199,10 @@
         renderthread::RenderThread::getInstance().queueAndWait(&task);
     }
 
+    static bool isRenderThreadRunning() {
+        return renderthread::RenderThread::hasInstance();
+    }
+
     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
 
     static void drawTextToCanvas(TestCanvas* canvas, const char* text,
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
new file mode 100644
index 0000000..db6402c
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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"
+
+class ClippingAnimation;
+
+static TestScene::Registrar _RectGrid(TestScene::Info{
+    "clip",
+    "Complex clip cases"
+    "Low CPU/GPU load.",
+    TestScene::simpleCreateScene<ClippingAnimation>
+});
+
+class ClippingAnimation : public TestScene {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, TestCanvas& canvas) override {
+        canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+        card = TestUtils::createNode(0, 0, 200, 400,
+                [](RenderProperties& props, TestCanvas& canvas) {
+            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            {
+                canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+                canvas.translate(100, 100);
+                canvas.rotate(45);
+                canvas.translate(-100, -100);
+                canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+                canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+            }
+            canvas.restore();
+
+            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            {
+                SkPath clipCircle;
+                clipCircle.addCircle(100, 300, 100);
+                canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op);
+                canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+            }
+            canvas.restore();
+
+            // put on a layer, to test stencil attachment
+            props.mutateLayerProperties().setType(LayerType::RenderLayer);
+            props.setAlpha(0.9f);
+        });
+        canvas.drawRenderNode(card.get());
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        card->mutateStagingProperties().setTranslationX(curFrame);
+        card->mutateStagingProperties().setTranslationY(curFrame);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+};
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 78fcd8b..c899850 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -29,14 +29,27 @@
 public:
     sp<RenderNode> card;
     void createContent(int width, int height, TestCanvas& canvas) override {
-        canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+        canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); // background
 
-        card = TestUtils::createNode(0, 0, 200, 200,
+        card = TestUtils::createNode(0, 0, 400, 800,
                 [](RenderProperties& props, TestCanvas& canvas) {
-            canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
-            canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
-            canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
-            canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
+            // nested clipped saveLayers
+            canvas.saveLayerAlpha(0, 0, 400, 400, 200, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode);
+            canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
+            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
+            canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+            canvas.restore();
+            canvas.restore();
+
+            // single unclipped saveLayer
+            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.translate(0, 400);
+            canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::SaveFlags(0)); // unclipped
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setColor(Color::Green_700);
+            canvas.drawCircle(200, 200, 200, paint);
             canvas.restore();
             canvas.restore();
         });
diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h
index ac78124..935ddcf 100644
--- a/libs/hwui/tests/common/scenes/TestSceneBase.h
+++ b/libs/hwui/tests/common/scenes/TestSceneBase.h
@@ -22,6 +22,7 @@
 #include "tests/common/TestContext.h"
 #include "tests/common/TestScene.h"
 #include "tests/common/TestUtils.h"
+#include "utils/Color.h"
 
 #include <functional>
 
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 1616a95..02a3950 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <unordered_map>
 #include <vector>
+#include <pthread.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/libs/hwui/tests/microbench/OpReordererBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
similarity index 82%
rename from libs/hwui/tests/microbench/OpReordererBench.cpp
rename to libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 6bfe5a9..67c95e2 100644
--- a/libs/hwui/tests/microbench/OpReordererBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -19,8 +19,8 @@
 #include "BakedOpState.h"
 #include "BakedOpDispatcher.h"
 #include "BakedOpRenderer.h"
+#include "FrameBuilder.h"
 #include "LayerUpdateQueue.h"
-#include "OpReorderer.h"
 #include "RecordedOp.h"
 #include "RecordingCanvas.h"
 #include "tests/common/TestContext.h"
@@ -61,20 +61,20 @@
     return vec;
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_defer);
-void BM_OpReorderer_defer::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_defer);
+void BM_FrameBuilder_defer::Run(int iters) {
     auto nodes = createTestNodeList();
     StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+        FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
                 nodes, sLightCenter);
-        MicroBench::DoNotOptimize(&reorderer);
+        MicroBench::DoNotOptimize(&frameBuilder);
     }
     StopBenchmarkTiming();
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
-void BM_OpReorderer_deferAndRender::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_deferAndRender);
+void BM_FrameBuilder_deferAndRender::Run(int iters) {
     TestUtils::runOnRenderThread([this, iters](RenderThread& thread) {
         auto nodes = createTestNodeList();
         BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128 };
@@ -84,11 +84,11 @@
 
         StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
                     nodes, sLightCenter);
 
             BakedOpRenderer renderer(caches, renderState, true, lightInfo);
-            reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+            frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
             MicroBench::DoNotOptimize(&renderer);
         }
         StopBenchmarkTiming();
@@ -117,10 +117,10 @@
     auto nodes = getSyncedSceneNodes(sceneName);
     benchmark.StartBenchmarkTiming();
     for (int i = 0; i < iters; i++) {
-        OpReorderer reorderer(sEmptyLayerUpdateQueue,
+        FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
                 SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
                 nodes, sLightCenter);
-        MicroBench::DoNotOptimize(&reorderer);
+        MicroBench::DoNotOptimize(&frameBuilder);
     }
     benchmark.StopBenchmarkTiming();
 }
@@ -136,25 +136,25 @@
 
         benchmark.StartBenchmarkTiming();
         for (int i = 0; i < iters; i++) {
-            OpReorderer reorderer(sEmptyLayerUpdateQueue,
+            FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
                     SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
                     nodes, sLightCenter);
 
             BakedOpRenderer renderer(caches, renderState, true, lightInfo);
-            reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+            frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
             MicroBench::DoNotOptimize(&renderer);
         }
         benchmark.StopBenchmarkTiming();
     });
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_listview_defer);
-void BM_OpReorderer_listview_defer::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_listview_defer);
+void BM_FrameBuilder_listview_defer::Run(int iters) {
     benchDeferScene(*this, iters, "listview");
 }
 
-BENCHMARK_NO_ARG(BM_OpReorderer_listview_deferAndRender);
-void BM_OpReorderer_listview_deferAndRender::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_listview_deferAndRender);
+void BM_FrameBuilder_listview_deferAndRender::Run(int iters) {
     benchDeferAndRenderScene(*this, iters, "listview");
 }
 
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index f9f5316..0f8e047 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include <BakedOpState.h>
+#include <ClipArea.h>
 #include <RecordedOp.h>
 #include <tests/common/TestUtils.h>
 
@@ -24,31 +25,33 @@
 namespace uirenderer {
 
 TEST(ResolvedRenderState, construct) {
+    LinearAllocator allocator;
     Matrix4 translate10x20;
     translate10x20.loadTranslate(10, 20, 0);
 
     SkPaint paint;
-    RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(100, 200), &paint);
+    ClipRect clip(Rect(100, 200));
+    RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, &clip, &paint);
     {
         // recorded with transform, no parent transform
         auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
-        ResolvedRenderState state(*parentSnapshot, recordedOp, false);
+        ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
         EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
-        EXPECT_EQ(Rect(100, 200), state.clipRect);
+        EXPECT_EQ(Rect(100, 200), state.clipRect());
         EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
         EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
     }
     {
         // recorded with transform and parent transform
         auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
-        ResolvedRenderState state(*parentSnapshot, recordedOp, false);
+        ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
 
         Matrix4 expectedTranslate;
         expectedTranslate.loadTranslate(20, 40, 0);
         EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform);
 
         // intersection of parent & transformed child clip
-        EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect);
+        EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect());
 
         // translated and also clipped
         EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds);
@@ -57,22 +60,24 @@
 }
 
 TEST(ResolvedRenderState, computeLocalSpaceClip) {
+    LinearAllocator allocator;
     Matrix4 translate10x20;
     translate10x20.loadTranslate(10, 20, 0);
 
     SkPaint paint;
-    RectOp recordedOp(Rect(1000, 1000), translate10x20, Rect(100, 200), &paint);
+    ClipRect clip(Rect(100, 200));
+    RectOp recordedOp(Rect(1000, 1000), translate10x20, &clip, &paint);
     {
         // recorded with transform, no parent transform
         auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
-        ResolvedRenderState state(*parentSnapshot, recordedOp, false);
+        ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
         EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip())
             << "Local clip rect should be 100x200, offset by -10,-20";
     }
     {
         // recorded with transform + parent transform
         auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
-        ResolvedRenderState state(*parentSnapshot, recordedOp, false);
+        ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
         EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip())
             << "Local clip rect should be 90x190, offset by -10,-20";
     }
@@ -149,6 +154,7 @@
 };
 
 TEST(ResolvedRenderState, construct_expandForStroke) {
+    LinearAllocator allocator;
     // Loop over table of test cases and verify different combinations of stroke width and transform
     for (auto&& testCase : sStrokeTestCases) {
         SkPaint strokedPaint;
@@ -156,58 +162,64 @@
         strokedPaint.setStyle(SkPaint::kStroke_Style);
         strokedPaint.setStrokeWidth(testCase.strokeWidth);
 
+        ClipRect clip(Rect(200, 200));
         RectOp recordedOp(Rect(50, 50, 150, 150),
-                Matrix4::identity(), Rect(200, 200), &strokedPaint);
+                Matrix4::identity(), &clip, &strokedPaint);
 
         Matrix4 snapshotMatrix;
         snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
         auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
 
-        ResolvedRenderState state(*parentSnapshot, recordedOp, true);
+        ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true);
         testCase.validator(state);
     }
 }
 
 TEST(BakedOpState, tryConstruct) {
-    LinearAllocator allocator;
-
     Matrix4 translate100x0;
     translate100x0.loadTranslate(100, 0, 0);
 
     SkPaint paint;
-    {
-        RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(100, 200), &paint);
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
-        BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
+    ClipRect clip(Rect(100, 200));
 
-        EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
-        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
-    }
-    {
-        RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(100, 200), &paint);
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
-        BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
+    LinearAllocator allocator;
+    RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
+    auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+    EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
+            << "successOp NOT rejected by clip, so should be constructed";
+    size_t successAllocSize = allocator.usedSize();
+    EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
 
-        EXPECT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
-        EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
-    }
+    RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
+    EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
+            << "rejectOp rejected by clip, so should not be constructed";
+
+    // NOTE: this relies on the clip having already been serialized by the op above
+    EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
 }
 
 TEST(BakedOpState, tryShadowOpConstruct) {
+    Matrix4 translate10x20;
+    translate10x20.loadTranslate(10, 20, 0);
+
     LinearAllocator allocator;
     {
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+        auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
-        EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
-        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
+        EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
+        EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
+                "since op is quick rejected based on snapshot clip";
     }
     {
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+        auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
         BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
-        ASSERT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
-        EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
+        ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
+        EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
+
+        EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
+        EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
     }
 }
 
@@ -218,7 +230,8 @@
         SkPaint paint;
         paint.setStyle(SkPaint::kStrokeAndFill_Style);
         paint.setStrokeWidth(0.0f);
-        RectOp rejectOp(Rect(100, 200), Matrix4::identity(), Rect(100, 200), &paint);
+        ClipRect clip(Rect(100, 200));
+        RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint);
         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
         auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
                 BakedOpState::StrokeBehavior::StyleDefined);
@@ -231,7 +244,8 @@
         SkPaint paint;
         paint.setStyle(SkPaint::kStrokeAndFill_Style);
         paint.setStrokeWidth(10.0f);
-        RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint);
+        ClipRect clip(Rect(200, 200));
+        RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
         auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
                 BakedOpState::StrokeBehavior::StyleDefined);
@@ -245,7 +259,8 @@
         SkPaint paint;
         paint.setStyle(SkPaint::kFill_Style);
         paint.setStrokeWidth(10.0f);
-        RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint);
+        ClipRect clip(Rect(200, 200));
+        RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
         auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
         auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
                 BakedOpState::StrokeBehavior::Forced);
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index c4d305e..4cae737 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -119,5 +119,122 @@
     EXPECT_EQ(expected, area.getClipRect());
 }
 
+TEST(ClipArea, serializeClip) {
+    ClipArea area(createClipArea());
+    LinearAllocator allocator;
+
+    // unset clip
+    EXPECT_EQ(nullptr, area.serializeClip(allocator));
+
+    // rect clip
+    area.setClip(0, 0, 200, 200);
+    {
+        auto serializedClip = area.serializeClip(allocator);
+        ASSERT_NE(nullptr, serializedClip);
+        ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode);
+        auto clipRect = reinterpret_cast<const ClipRect*>(serializedClip);
+        ASSERT_EQ(Rect(200, 200), clipRect->rect);
+        EXPECT_EQ(serializedClip, area.serializeClip(allocator))
+                << "Requery of clip on unmodified ClipArea must return same pointer.";
+    }
+
+    // rect list
+    Matrix4 rotate;
+    rotate.loadRotate(2.0f);
+    area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
+    {
+        auto serializedClip = area.serializeClip(allocator);
+        ASSERT_NE(nullptr, serializedClip);
+        ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
+        auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
+        ASSERT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
+        EXPECT_EQ(serializedClip, area.serializeClip(allocator))
+                << "Requery of clip on unmodified ClipArea must return same pointer.";
+    }
+
+    // region
+    SkPath circlePath;
+    circlePath.addCircle(100, 100, 100);
+    area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
+    {
+        auto serializedClip = area.serializeClip(allocator);
+        ASSERT_NE(nullptr, serializedClip);
+        ASSERT_EQ(ClipMode::Region, serializedClip->mode);
+        auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip);
+        ASSERT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds())
+                << "Clip region should be 200x200";
+        EXPECT_EQ(serializedClip, area.serializeClip(allocator))
+                << "Requery of clip on unmodified ClipArea must return same pointer.";
+    }
 }
+
+TEST(ClipArea, serializeIntersectedClip) {
+    ClipArea area(createClipArea());
+    LinearAllocator allocator;
+
+    // simple state;
+    EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
+    area.setClip(0, 0, 200, 200);
+    {
+        auto origRectClip = area.serializeClip(allocator);
+        ASSERT_NE(nullptr, origRectClip);
+        EXPECT_EQ(origRectClip, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
+    }
+
+    // rect
+    {
+        ClipRect recordedClip(Rect(100, 100));
+        Matrix4 translateScale;
+        translateScale.loadTranslate(100, 100, 0);
+        translateScale.scale(2, 3, 1);
+        auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
+        ASSERT_NE(nullptr, resolvedClip);
+        ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode);
+        EXPECT_EQ(Rect(100, 100, 200, 200),
+                reinterpret_cast<const ClipRect*>(resolvedClip)->rect);
+
+        EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale))
+                << "Must return previous serialization, since input is same";
+
+        ClipRect recordedClip2(Rect(100, 100));
+        EXPECT_NE(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip2, translateScale))
+                << "Shouldn't return previous serialization, since matrix location is different";
+    }
+
+    // rect list
+    Matrix4 rotate;
+    rotate.loadRotate(2.0f);
+    area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
+    {
+        ClipRect recordedClip(Rect(100, 100));
+        auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity());
+        ASSERT_NE(nullptr, resolvedClip);
+        ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode);
+        auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip);
+        EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
+    }
+
+    // region
+    SkPath circlePath;
+    circlePath.addCircle(100, 100, 100);
+    area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
+    {
+        SkPath ovalPath;
+        ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200));
+
+        ClipRegion recordedClip;
+        recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200)));
+
+        Matrix4 translate10x20;
+        translate10x20.loadTranslate(10, 20, 0);
+        auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip,
+                translate10x20); // Note: only translate for now, others not handled correctly
+        ASSERT_NE(nullptr, resolvedClip);
+        ASSERT_EQ(ClipMode::Region, resolvedClip->mode);
+        auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
+        EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds());
+    }
 }
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/tests/unit/CrashHandlerInjector.cpp b/libs/hwui/tests/unit/CrashHandlerInjector.cpp
new file mode 100644
index 0000000..685c264
--- /dev/null
+++ b/libs/hwui/tests/unit/CrashHandlerInjector.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "tests/common/TestUtils.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::uirenderer;
+
+static void gunitCrashHandler() {
+    FAIL() << "RenderThread fatal exception!";
+}
+
+static void hookError() {
+    TestUtils::setRenderThreadCrashHandler(gunitCrashHandler);
+}
+
+class HookErrorInit {
+public:
+    HookErrorInit() { hookError(); }
+};
+
+static HookErrorInit sInit;
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
similarity index 75%
rename from libs/hwui/tests/unit/OpReordererTests.cpp
rename to libs/hwui/tests/unit/FrameBuilderTests.cpp
index b28e436..b51bd2f 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -18,8 +18,8 @@
 
 #include <BakedOpState.h>
 #include <DeferredLayerUpdater.h>
+#include <FrameBuilder.h>
 #include <LayerUpdateQueue.h>
-#include <OpReorderer.h>
 #include <RecordedOp.h>
 #include <RecordingCanvas.h>
 #include <tests/common/TestUtils.h>
@@ -32,13 +32,6 @@
 const LayerUpdateQueue sEmptyLayerUpdateQueue;
 const Vector3 sLightCenter = {100, 100, 100};
 
-static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) {
-    TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-    std::vector<sp<RenderNode>> vec;
-    vec.emplace_back(node);
-    return vec;
-}
-
 /**
  * Virtual class implemented by each test to redirect static operation / state transitions to
  * virtual methods.
@@ -64,14 +57,14 @@
         ADD_FAILURE() << "Layer updates not expected in this test";
     }
     virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
-    virtual void endFrame() {}
+    virtual void endFrame(const Rect& repaintRect) {}
 
     // define virtual defaults for single draw methods
 #define X(Type) \
     virtual void on##Type(const Type&, const BakedOpState&) { \
         ADD_FAILURE() << #Type " not expected in this test"; \
     }
-    MAP_OPS(X)
+    MAP_RENDERABLE_OPS(X)
 #undef X
 
     // define virtual defaults for merged draw methods
@@ -79,7 +72,7 @@
     virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
         ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
     }
-    MAP_MERGED_OPS(X)
+    MAP_MERGEABLE_OPS(X)
 #undef X
 
     int getIndex() { return mIndex; }
@@ -99,7 +92,7 @@
     static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
         renderer.on##Type(op, state); \
     }
-    MAP_OPS(X);
+    MAP_RENDERABLE_OPS(X);
 #undef X
 
     // define merged op methods, which redirect to TestRendererBase
@@ -107,13 +100,13 @@
     static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
         renderer.onMerged##Type##s(opList); \
     }
-    MAP_MERGED_OPS(X);
+    MAP_MERGEABLE_OPS(X);
 #undef X
 };
 
 class FailRenderer : public TestRendererBase {};
 
-TEST(OpReorderer, simple) {
+TEST(FrameBuilder, simple) {
     class SimpleTestRenderer : public TestRendererBase {
     public:
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -127,7 +120,7 @@
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
             EXPECT_EQ(2, mIndex++);
         }
-        void endFrame() override {
+        void endFrame(const Rect& repaintRect) override {
             EXPECT_EQ(3, mIndex++);
         }
     };
@@ -138,14 +131,14 @@
         canvas.drawRect(0, 0, 100, 200, SkPaint());
         canvas.drawBitmap(bitmap, 10, 10, nullptr);
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     SimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
-TEST(OpReorderer, simpleStroke) {
+TEST(FrameBuilder, simpleStroke) {
     class SimpleStrokeTestRenderer : public TestRendererBase {
     public:
         void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -164,14 +157,14 @@
         strokedPaint.setStrokeWidth(10);
         canvas.drawPoint(50, 50, strokedPaint);
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     SimpleStrokeTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(OpReorderer, simpleRejection) {
+TEST(FrameBuilder, simpleRejection) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -179,14 +172,14 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
 
     FailRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, simpleBatching) {
+TEST(FrameBuilder, simpleBatching) {
     const int LOOPS = 5;
     class SimpleBatchingTestRenderer : public TestRendererBase {
     public:
@@ -214,15 +207,15 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     SimpleBatchingTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
-TEST(OpReorderer, clippedMerging) {
+TEST(FrameBuilder, clippedMerging) {
     class ClippedMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -255,14 +248,14 @@
         canvas.drawBitmap(bitmap, 40, 70, nullptr);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     ClippedMergingTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(OpReorderer, textMerging) {
+TEST(FrameBuilder, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
         void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -283,14 +276,14 @@
         TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
         TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     TextMergingTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
 }
 
-TEST(OpReorderer, textStrikethrough) {
+TEST(FrameBuilder, textStrikethrough) {
     const int LOOPS = 5;
     class TextStrikethroughTestRenderer : public TestRendererBase {
     public:
@@ -314,20 +307,20 @@
             TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
         }
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     TextStrikethroughTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2 * LOOPS, renderer.getIndex())
             << "Expect number of ops = 2 * loop count";
 }
 
-RENDERTHREAD_TEST(OpReorderer, textureLayer) {
+RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
     class TextureLayerTestRenderer : public TestRendererBase {
     public:
         void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
             EXPECT_EQ(0, mIndex++);
-            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect);
+            EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
             EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
 
             Matrix4 expected;
@@ -348,14 +341,14 @@
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     TextureLayerTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex());
 }
 
-TEST(OpReorderer, renderNode) {
+TEST(FrameBuilder, renderNode) {
     class RenderNodeTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -393,19 +386,19 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(parent), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(parent), sLightCenter);
     RenderNodeTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, clipped) {
+TEST(FrameBuilder, clipped) {
     class ClippedTestRenderer : public TestRendererBase {
     public:
         void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
             EXPECT_EQ(0, mIndex++);
             EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
-            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
+            EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
             EXPECT_TRUE(state.computedState.transform.isIdentity());
         }
     };
@@ -416,14 +409,14 @@
         canvas.drawBitmap(bitmap, 0, 0, nullptr);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue,
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
             SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
-            200, 200, createSyncedNodeList(node), sLightCenter);
+            200, 200, TestUtils::createSyncedNodeList(node), sLightCenter);
     ClippedTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-TEST(OpReorderer, saveLayerSimple) {
+TEST(FrameBuilder, saveLayer_simple) {
     class SaveLayerSimpleTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -439,7 +432,7 @@
             EXPECT_EQ(1, mIndex++);
             EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
             EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
-            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect);
+            EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
 
             Matrix4 expectedTransform;
             expectedTransform.loadTranslate(-10, -10, 0);
@@ -448,7 +441,7 @@
         void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
             EXPECT_EQ(3, mIndex++);
             EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
-            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect);
+            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
             EXPECT_TRUE(state.computedState.transform.isIdentity());
         }
     };
@@ -459,14 +452,14 @@
         canvas.drawRect(10, 10, 190, 190, SkPaint());
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     SaveLayerSimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
-TEST(OpReorderer, saveLayerNested) {
+TEST(FrameBuilder, saveLayer_nested) {
     /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
      * - startTemporaryLayer2, rect2 endLayer2
      * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -494,7 +487,7 @@
         void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
             EXPECT_EQ(7, mIndex++);
         }
-        void endFrame() override {
+        void endFrame(const Rect& repaintRect) override {
             EXPECT_EQ(9, mIndex++);
         }
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -531,14 +524,14 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     SaveLayerNestedTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 }
 
-TEST(OpReorderer, saveLayerContentRejection) {
+TEST(FrameBuilder, saveLayer_contentRejection) {
         auto node = TestUtils::createNode(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -551,15 +544,173 @@
         canvas.restore();
         canvas.restore();
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
 
     FailRenderer renderer;
     // should see no ops, even within the layer, since the layer should be rejected
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 }
 
-RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) {
+TEST(FrameBuilder, saveLayerUnclipped_simple) {
+    class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
+    public:
+        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
+            EXPECT_TRUE(state.computedState.transform.isIdentity());
+        }
+        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(1, mIndex++);
+            ASSERT_NE(nullptr, op.paint);
+            ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+        }
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(2, mIndex++);
+            EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
+            EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
+            EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
+            EXPECT_TRUE(state.computedState.transform.isIdentity());
+        }
+        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(3, mIndex++);
+            EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+            EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
+            EXPECT_TRUE(state.computedState.transform.isIdentity());
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0));
+        canvas.drawRect(0, 0, 200, 200, SkPaint());
+        canvas.restore();
+    });
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
+    SaveLayerUnclippedSimpleTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(4, renderer.getIndex());
+}
+
+TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
+    class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
+    public:
+        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
+            int index = mIndex++;
+            EXPECT_GT(4, index);
+            EXPECT_EQ(5, op.unmappedBounds.getWidth());
+            EXPECT_EQ(5, op.unmappedBounds.getHeight());
+            if (index == 0) {
+                EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
+            } else if (index == 1) {
+                EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
+            } else if (index == 2) {
+                EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
+            } else if (index == 3) {
+                EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
+            }
+        }
+        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(4, mIndex++);
+            ASSERT_EQ(op.vertexCount, 16u);
+            for (size_t i = 0; i < op.vertexCount; i++) {
+                auto v = op.vertices[i];
+                EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
+                EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
+            }
+        }
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(5, mIndex++);
+        }
+        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
+            EXPECT_LT(5, mIndex++);
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+
+        int restoreTo = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.scale(2, 2);
+        canvas.saveLayerAlpha(0, 0, 5, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
+        canvas.saveLayerAlpha(95, 0, 100, 5, 128, SkCanvas::kMatrixClip_SaveFlag);
+        canvas.saveLayerAlpha(0, 95, 5, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
+        canvas.saveLayerAlpha(95, 95, 100, 100, 128, SkCanvas::kMatrixClip_SaveFlag);
+        canvas.drawRect(0, 0, 100, 100, SkPaint());
+        canvas.restoreToCount(restoreTo);
+    });
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
+    SaveLayerUnclippedMergedClearsTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(10, renderer.getIndex())
+            << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
+}
+
+/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
+ * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
+ * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
+ */
+TEST(FrameBuilder, saveLayerUnclipped_complex) {
+    class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
+    public:
+        OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
+            EXPECT_EQ(0, mIndex++); // savelayer first
+            return (OffscreenBuffer*)0xabcd;
+        }
+        void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
+            int index = mIndex++;
+            EXPECT_TRUE(index == 1 || index == 7);
+        }
+        void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
+            int index = mIndex++;
+            EXPECT_TRUE(index == 2 || index == 8);
+        }
+        void onRectOp(const RectOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(3, mIndex++);
+            Matrix4 expected;
+            expected.loadTranslate(-100, -100, 0);
+            EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
+            EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
+        }
+        void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
+            int index = mIndex++;
+            EXPECT_TRUE(index == 4 || index == 10);
+        }
+        void endLayer() override {
+            EXPECT_EQ(5, mIndex++);
+        }
+        void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+            EXPECT_EQ(6, mIndex++);
+        }
+        void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(9, mIndex++);
+        }
+        void endFrame(const Rect& repaintRect) override {
+            EXPECT_EQ(11, mIndex++);
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.saveLayerAlpha(100, 100, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); // clipped
+        canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.drawRect(200, 200, 300, 300, SkPaint());
+        canvas.restore();
+        canvas.restore();
+        canvas.restore();
+    });
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
+    SaveLayerUnclippedComplexTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(12, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
     class HwLayerSimpleTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -574,7 +725,7 @@
             EXPECT_TRUE(state.computedState.transform.isIdentity())
                     << "Transform should be reset within layer";
 
-            EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
+            EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
                     << "Damage rect should be used to clip layer content";
         }
         void endLayer() override {
@@ -586,7 +737,7 @@
         void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
             EXPECT_EQ(4, mIndex++);
         }
-        void endFrame() override {
+        void endFrame(const Rect& repaintRect) override {
             EXPECT_EQ(5, mIndex++);
         }
     };
@@ -604,23 +755,23 @@
     OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
     *layerHandle = &layer;
 
-    auto syncedNodeList = createSyncedNodeList(node);
+    auto syncedNodeList = TestUtils::createSyncedNodeList(node);
 
     // only enqueue partial damage
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
 
-    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedNodeList, sLightCenter);
     HwLayerSimpleTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(6, renderer.getIndex());
 
     // clean up layer pointer, so we can safely destruct RenderNode
     *layerHandle = nullptr;
 }
 
-RENDERTHREAD_TEST(OpReorderer, hwLayerComplex) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
     /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
      * - startRepaintLayer(child), rect(grey), endLayer
      * - startTemporaryLayer, drawLayer(child), endLayer
@@ -675,7 +826,7 @@
                 EXPECT_EQ(200u, layer->viewportHeight);
             } else { ADD_FAILURE(); }
         }
-        void endFrame() override {
+        void endFrame(const Rect& repaintRect) override {
             EXPECT_EQ(12, mIndex++);
         }
     };
@@ -705,16 +856,16 @@
     OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
     *(parent->getLayerHandle()) = &parentLayer;
 
-    auto syncedList = createSyncedNodeList(parent);
+    auto syncedList = TestUtils::createSyncedNodeList(parent);
 
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
 
-    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList, sLightCenter);
     HwLayerComplexTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(13, renderer.getIndex());
 
     // clean up layer pointers, so we can safely destruct RenderNodes
@@ -736,7 +887,7 @@
     node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
     canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
 }
-TEST(OpReorderer, zReorder) {
+TEST(FrameBuilder, zReorder) {
     class ZReorderTestRenderer : public TestRendererBase {
     public:
         void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -760,14 +911,14 @@
         drawOrderedRect(&canvas, 8);
         drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
     });
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            createSyncedNodeList(parent), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+            TestUtils::createSyncedNodeList(parent), sLightCenter);
     ZReorderTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(10, renderer.getIndex());
 };
 
-TEST(OpReorderer, projectionReorder) {
+TEST(FrameBuilder, projectionReorder) {
     static const int scrollX = 5;
     static const int scrollY = 10;
     class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -843,10 +994,10 @@
         canvas.restore();
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
-            createSyncedNodeList(parent), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+            TestUtils::createSyncedNodeList(parent), sLightCenter);
     ProjectionReorderTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(3, renderer.getIndex());
 }
 
@@ -862,7 +1013,7 @@
     });
 }
 
-TEST(OpReorderer, shadow) {
+TEST(FrameBuilder, shadow) {
     class ShadowTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -886,14 +1037,14 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(parent), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(parent), sLightCenter);
     ShadowTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(2, renderer.getIndex());
 }
 
-TEST(OpReorderer, shadowSaveLayer) {
+TEST(FrameBuilder, shadowSaveLayer) {
     class ShadowSaveLayerTestRenderer : public TestRendererBase {
     public:
         OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -927,14 +1078,14 @@
         canvas.restoreToCount(count);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
     ShadowSaveLayerTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
 }
 
-RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
+RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
     class ShadowHwLayerTestRenderer : public TestRendererBase {
     public:
         void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -974,20 +1125,20 @@
     layer.setWindowTransform(windowTransform);
     *layerHandle = &layer;
 
-    auto syncedList = createSyncedNodeList(parent);
+    auto syncedList = TestUtils::createSyncedNodeList(parent);
     LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
     layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-    OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+    FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
             syncedList, (Vector3) { 100, 100, 100 });
     ShadowHwLayerTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(5, renderer.getIndex());
 
     // clean up layer pointer, so we can safely destruct RenderNode
     *layerHandle = nullptr;
 }
 
-TEST(OpReorderer, shadowLayering) {
+TEST(FrameBuilder, shadowLayering) {
     class ShadowLayeringTestRenderer : public TestRendererBase {
     public:
         void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1006,10 +1157,10 @@
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
-            createSyncedNodeList(parent), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(parent), sLightCenter);
     ShadowLayeringTestRenderer renderer;
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(4, renderer.getIndex());
 }
 
@@ -1034,14 +1185,14 @@
         canvas.drawRect(0, 0, 100, 100, paint);
     });
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
-            createSyncedNodeList(node), sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
     PropertyTestRenderer renderer(opValidateCallback);
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
     EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
 }
 
-TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
+TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
     testProperty([](RenderProperties& properties) {
         properties.setAlpha(0.5f);
         properties.setHasOverlappingRendering(false);
@@ -1050,7 +1201,7 @@
     });
 }
 
-TEST(OpReorderer, renderPropClipping) {
+TEST(FrameBuilder, renderPropClipping) {
     testProperty([](RenderProperties& properties) {
         properties.setClipToBounds(true);
         properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1060,7 +1211,7 @@
     });
 }
 
-TEST(OpReorderer, renderPropRevealClip) {
+TEST(FrameBuilder, renderPropRevealClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableRevealClip().set(true, 50, 50, 25);
     }, [](const RectOp& op, const BakedOpState& state) {
@@ -1071,7 +1222,7 @@
     });
 }
 
-TEST(OpReorderer, renderPropOutlineClip) {
+TEST(FrameBuilder, renderPropOutlineClip) {
     testProperty([](RenderProperties& properties) {
         properties.mutableOutline().setShouldClip(true);
         properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1083,7 +1234,7 @@
     });
 }
 
-TEST(OpReorderer, renderPropTransform) {
+TEST(FrameBuilder, renderPropTransform) {
     testProperty([](RenderProperties& properties) {
         properties.setLeftTopRightBottom(10, 10, 110, 110);
 
@@ -1174,17 +1325,17 @@
         paint.setColor(SK_ColorWHITE);
         canvas.drawRect(0, 0, 10000, 10000, paint);
     });
-    auto nodes = createSyncedNodeList(node); // sync before querying height
+    auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height
 
-    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
     SaveLayerAlphaClipTestRenderer renderer(outObservedData);
-    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
 
     // assert, since output won't be valid if we haven't seen a save layer triggered
     ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
 }
 
-TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setTranslationX(10); // offset rendering content
@@ -1200,7 +1351,7 @@
             << "expect content to be translated as part of being clipped";
 }
 
-TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         // Translate and rotate the view so that the only visible part is the top left corner of
@@ -1219,7 +1370,7 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
-TEST(OpReorderer, renderPropSaveLayerAlphaScale) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
     SaveLayerAlphaData observedData;
     testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
         properties.setPivotX(0);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
new file mode 100644
index 0000000..aa1dcb2
--- /dev/null
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <GpuMemoryTracker.h>
+
+#include "renderthread/EglManager.h"
+#include "renderthread/RenderThread.h"
+#include "tests/common/TestUtils.h"
+
+#include <utils/StrongPointer.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+class TestGPUObject : public GpuMemoryTracker {
+public:
+    TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {}
+
+    void changeSize(int newSize) {
+        notifySizeChanged(newSize);
+    }
+};
+
+// Other tests may have created a renderthread and EGL context.
+// This will destroy the EGLContext on RenderThread if it exists so that the
+// current thread can spoof being a GPU thread
+static void destroyEglContext() {
+    if (TestUtils::isRenderThreadRunning()) {
+        TestUtils::runOnRenderThread([](RenderThread& thread) {
+            thread.eglManager().destroy();
+        });
+    }
+}
+
+TEST(GpuMemoryTracker, sizeCheck) {
+    destroyEglContext();
+
+    GpuMemoryTracker::onGLContextCreated();
+    ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+    ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
+    {
+        TestGPUObject myObj;
+        ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
+        myObj.changeSize(500);
+        ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+        myObj.changeSize(1000);
+        ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+        myObj.changeSize(300);
+        ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+    }
+    ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+    ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
+    GpuMemoryTracker::onGLContextDestroyed();
+}
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
new file mode 100644
index 0000000..41e44fc
--- /dev/null
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "BakedOpRenderer.h"
+#include "BakedOpDispatcher.h"
+#include "FrameBuilder.h"
+#include "LayerUpdateQueue.h"
+#include "RecordingCanvas.h"
+#include "tests/common/TestUtils.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+const LayerUpdateQueue sEmptyLayerUpdateQueue;
+const Vector3 sLightCenter = {100, 100, 100};
+
+RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
+    auto node = TestUtils::createNode(0, 0, 200, 200,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0));
+        canvas.drawRect(0, 0, 200, 200, SkPaint());
+        canvas.restore();
+    });
+    BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128};
+    RenderState& renderState = renderThread.renderState();
+    Caches& caches = Caches::getInstance();
+
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+            TestUtils::createSyncedNodeList(node), sLightCenter);
+    BakedOpRenderer renderer(caches, renderState, true, lightInfo);
+    frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+}
diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
index 78d65dd..5c44290 100644
--- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp
+++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
@@ -27,7 +27,7 @@
     int two = 2;
 };
 
-TEST(LinearAllocator, alloc) {
+TEST(LinearAllocator, create) {
     LinearAllocator la;
     EXPECT_EQ(0u, la.usedSize());
     la.alloc(64);
@@ -35,7 +35,7 @@
     // so the usedSize isn't strictly defined
     EXPECT_LE(64u, la.usedSize());
     EXPECT_GT(80u, la.usedSize());
-    auto pair = la.alloc<SimplePair>();
+    auto pair = la.create<SimplePair>();
     EXPECT_LE(64u + sizeof(SimplePair), la.usedSize());
     EXPECT_GT(80u + sizeof(SimplePair), la.usedSize());
     EXPECT_EQ(1, pair->one);
@@ -47,8 +47,8 @@
     {
         LinearAllocator la;
         for (int i = 0; i < 5; i++) {
-            la.alloc<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
-            la.alloc<SimplePair>();
+            la.create<TestUtils::SignalingDtor>()->setSignal(destroyed + i);
+            la.create<SimplePair>();
         }
         la.alloc(100);
         for (int i = 0; i < 5; i++) {
@@ -75,7 +75,7 @@
         la.rewindIfLastAlloc(addr, 100);
         EXPECT_GT(16u, la.usedSize());
         size_t emptySize = la.usedSize();
-        auto sigdtor = la.alloc<TestUtils::SignalingDtor>();
+        auto sigdtor = la.create<TestUtils::SignalingDtor>();
         sigdtor->setSignal(&destroyed);
         EXPECT_EQ(0, destroyed);
         EXPECT_LE(emptySize, la.usedSize());
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index 2187654..2fd8795 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -15,11 +15,11 @@
  */
 
 #include <gtest/gtest.h>
+#include <Rect.h>
 #include <renderstate/OffscreenBufferPool.h>
 
 #include <tests/common/TestUtils.h>
 
-using namespace android;
 using namespace android::uirenderer;
 
 TEST(OffscreenBuffer, computeIdealDimension) {
@@ -36,13 +36,25 @@
         EXPECT_EQ(49u, layer.viewportWidth);
         EXPECT_EQ(149u, layer.viewportHeight);
 
-        EXPECT_EQ(64u, layer.texture.width);
-        EXPECT_EQ(192u, layer.texture.height);
+        EXPECT_EQ(64u, layer.texture.width());
+        EXPECT_EQ(192u, layer.texture.height());
 
         EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
     });
 }
 
+TEST(OffscreenBuffer, getTextureCoordinates) {
+    TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+        OffscreenBuffer layerAligned(thread.renderState(), Caches::getInstance(), 256u, 256u);
+        EXPECT_EQ(Rect(0, 1, 1, 0),
+                layerAligned.getTextureCoordinates());
+
+        OffscreenBuffer layerUnaligned(thread.renderState(), Caches::getInstance(), 200u, 225u);
+        EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0),
+                layerUnaligned.getTextureCoordinates());
+    });
+}
+
 TEST(OffscreenBufferPool, construct) {
     TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
         OffscreenBufferPool pool;
@@ -51,7 +63,6 @@
         EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
                 << "pool must read size from Properties";
     });
-
 }
 
 TEST(OffscreenBufferPool, getPutClear) {
@@ -89,8 +100,8 @@
         ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
         EXPECT_EQ(60u, layer->viewportWidth);
         EXPECT_EQ(55u, layer->viewportHeight);
-        EXPECT_EQ(64u, layer->texture.width);
-        EXPECT_EQ(64u, layer->texture.height);
+        EXPECT_EQ(64u, layer->texture.width());
+        EXPECT_EQ(64u, layer->texture.height());
 
         // resized to use different object in pool
         auto layer2 = pool.get(thread.renderState(), 128u, 128u);
@@ -99,12 +110,14 @@
         ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
         EXPECT_EQ(120u, layer2->viewportWidth);
         EXPECT_EQ(125u, layer2->viewportHeight);
-        EXPECT_EQ(128u, layer2->texture.width);
-        EXPECT_EQ(128u, layer2->texture.height);
+        EXPECT_EQ(128u, layer2->texture.width());
+        EXPECT_EQ(128u, layer2->texture.height());
 
         // original allocation now only thing in pool
         EXPECT_EQ(1u, pool.getCount());
         EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
+
+        pool.putOrDelete(layer2);
     });
 }
 
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 08f927c..ff098c8 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -41,6 +41,22 @@
     playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
 }
 
+TEST(RecordingCanvas, clipRect) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
+        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
+        canvas.drawRect(0, 0, 50, 50, SkPaint());
+        canvas.drawRect(50, 50, 100, 100, SkPaint());
+        canvas.restore();
+    });
+
+    ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
+    EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
+    EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
+    EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
+            << "Clip should be serialized once";
+}
+
 TEST(RecordingCanvas, drawLines) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
@@ -66,7 +82,7 @@
     ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
     auto op = *(dl->getOps()[0]);
     ASSERT_EQ(RecordedOpId::RectOp, op.opId);
-    EXPECT_EQ(Rect(100, 200), op.localClipRect);
+    EXPECT_EQ(nullptr, op.localClip);
     EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
 }
 
@@ -83,7 +99,7 @@
     playbackOps(*dl, [&count](const RecordedOp& op) {
         count++;
         ASSERT_EQ(RecordedOpId::TextOp, op.opId);
-        EXPECT_EQ(Rect(200, 200), op.localClipRect);
+        EXPECT_EQ(nullptr, op.localClip);
         EXPECT_TRUE(op.localMatrix.isIdentity());
         EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
                 << "Op expected to be 25+ pixels wide, 10+ pixels tall";
@@ -185,7 +201,7 @@
             ASSERT_NE(nullptr, op.paint);
             EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
             EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
-            EXPECT_EQ(Rect(100, 200), op.localClipRect);
+            EXPECT_EQ(nullptr, op.localClip);
 
             Matrix4 expectedMatrix;
             expectedMatrix.loadIdentity();
@@ -194,7 +210,7 @@
             ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
             EXPECT_EQ(nullptr, op.paint);
             EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
-            EXPECT_EQ(Rect(100, 200), op.localClipRect);
+            EXPECT_EQ(nullptr, op.localClip);
 
             Matrix4 expectedMatrix;
             expectedMatrix.loadTranslate(25, 25, 0);
@@ -208,7 +224,7 @@
 
 TEST(RecordingCanvas, saveLayer_simple) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
-        canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kClipToLayer_SaveFlag);
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
     });
@@ -219,12 +235,12 @@
         case 0:
             EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
-            EXPECT_EQ(Rect(200, 200), op.localClipRect);
+            EXPECT_EQ(nullptr, op.localClip);
             EXPECT_TRUE(op.localMatrix.isIdentity());
             break;
         case 1:
             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
-            EXPECT_EQ(Rect(180, 160), op.localClipRect);
+            EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
             EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
             expectedMatrix.loadTranslate(-10, -20, 0);
             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
@@ -240,12 +256,78 @@
     EXPECT_EQ(3, count);
 }
 
+TEST(RecordingCanvas, saveLayer_missingRestore) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+        canvas.drawRect(0, 0, 200, 200, SkPaint());
+        // Note: restore omitted, shouldn't result in unmatched save
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        if (count++ == 2) {
+            EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
+        }
+    });
+    EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
+}
+
+TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.drawRect(10, 20, 190, 180, SkPaint());
+        canvas.restore();
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        switch(count++) {
+        case 0:
+            EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
+            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+            EXPECT_EQ(nullptr, op.localClip);
+            EXPECT_TRUE(op.localMatrix.isIdentity());
+            break;
+        case 1:
+            EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+            EXPECT_EQ(nullptr, op.localClip);
+            EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+            EXPECT_TRUE(op.localMatrix.isIdentity());
+            break;
+        case 2:
+            EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
+            // Don't bother asserting recording state data - it's not used
+            break;
+        default:
+            ADD_FAILURE();
+        }
+    });
+    EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, saveLayer_addClipFlag) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
+        canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SkCanvas::SaveFlags)0); // unclipped
+        canvas.drawRect(10, 20, 190, 180, SkPaint());
+        canvas.restore();
+        canvas.restore();
+    });
+    int count = 0;
+    playbackOps(*dl, [&count](const RecordedOp& op) {
+        if (count++ == 0) {
+            EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
+                    << "Clip + unclipped saveLayer should result in a clipped layer";
+        }
+    });
+    EXPECT_EQ(3, count);
+}
+
 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, SkRegion::kReplace_Op);
 
-        canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kClipToLayer_SaveFlag);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
@@ -254,8 +336,8 @@
         if (count++ == 1) {
             Matrix4 expectedMatrix;
             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
-            EXPECT_EQ(Rect(100, 100), op.localClipRect) << "Recorded clip rect should be"
-                    " intersection of viewport and saveLayer bounds, in layer space";
+            EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
+            // intersection of viewport and saveLayer bounds, in layer space;
             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
             expectedMatrix.loadTranslate(-100, -100, 0);
             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
@@ -271,7 +353,7 @@
         canvas.rotate(45);
         canvas.translate(-50, -50);
 
-        canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kClipToLayer_SaveFlag);
         canvas.drawRect(0, 0, 100, 100, SkPaint());
         canvas.restore();
 
@@ -281,7 +363,7 @@
     playbackOps(*dl, [&count](const RecordedOp& op) {
         if (count++ == 1) {
             EXPECT_EQ(RecordedOpId::RectOp, op.opId);
-            EXPECT_EQ(Rect(100, 100), op.localClipRect);
+            EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
             EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
             EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
                     << "Recorded op shouldn't see any canvas transform before the saveLayer";
@@ -298,7 +380,7 @@
         canvas.translate(-200, -200);
 
         // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
-        canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+        canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
 
@@ -312,7 +394,16 @@
 
             // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
             // the parent 200x200 viewport, but prior to rotation
-            EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136), op.localClipRect);
+            ASSERT_NE(nullptr, op.localClip);
+            ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
+            // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
+            // causes the clip to be recorded by contained draw commands, though it's not necessary
+            // since the same clip will be computed at draw time. If such a change is made, this
+            // check could be done at record time by querying the clip, or the clip could be altered
+            // slightly so that it is serialized.
+            EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136),
+                    (reinterpret_cast<const ClipRect*>(op.localClip))->rect);
+
             EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
             expectedMatrix.loadIdentity();
             EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
diff --git a/libs/hwui/tests/unit/StringUtilsTests.cpp b/libs/hwui/tests/unit/StringUtilsTests.cpp
index 6b2e265..b60e96c 100644
--- a/libs/hwui/tests/unit/StringUtilsTests.cpp
+++ b/libs/hwui/tests/unit/StringUtilsTests.cpp
@@ -36,3 +36,18 @@
     EXPECT_TRUE(collection.has("GL_ext1"));
     EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list
 }
+
+TEST(StringUtils, sizePrinter) {
+    std::stringstream os;
+    os << SizePrinter{500};
+    EXPECT_EQ("500.00B", os.str());
+    os.str("");
+    os << SizePrinter{46080};
+    EXPECT_EQ("45.00KiB", os.str());
+    os.str("");
+    os << SizePrinter{5 * 1024 * 1024 + 520 * 1024};
+    EXPECT_EQ("5.51MiB", os.str());
+    os.str("");
+    os << SizePrinter{2147483647};
+    EXPECT_EQ("2048.00MiB", os.str());
+}
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index e1c6f6c..dcbc0dd 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -56,12 +56,12 @@
     void* alloc(size_t size);
 
     /**
-     * Allocates an instance of the template type with the default constructor
+     * Allocates an instance of the template type with the given construction parameters
      * and adds it to the automatic destruction list.
      */
-    template<class T>
-    T* alloc() {
-        T* ret = new (*this) T;
+    template<class T, typename... Params>
+    T* create(Params... params) {
+        T* ret = new (*this) T(params...);
         autoDestroy(ret);
         return ret;
     }
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
index 055869f..05a3d59 100644
--- a/libs/hwui/utils/StringUtils.h
+++ b/libs/hwui/utils/StringUtils.h
@@ -18,6 +18,8 @@
 
 #include <string>
 #include <unordered_set>
+#include <ostream>
+#include <iomanip>
 
 namespace android {
 namespace uirenderer {
@@ -34,6 +36,21 @@
     static unordered_string_set split(const char* spacedList);
 };
 
+struct SizePrinter {
+    int bytes;
+    friend std::ostream& operator<<(std::ostream& stream, const SizePrinter& d) {
+        static const char* SUFFIXES[] = {"B", "KiB", "MiB"};
+        size_t suffix = 0;
+        double temp = d.bytes;
+        while (temp > 1000 && suffix < 2) {
+            temp /= 1024.0;
+            suffix++;
+        }
+        stream << std::fixed << std::setprecision(2) << temp << SUFFIXES[suffix];
+        return stream;
+    }
+};
+
 } /* namespace uirenderer */
 } /* namespace android */
 
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 05b4a72..dcc4946 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -18,6 +18,7 @@
 #include "AnimationContext.h"
 #include "DisplayListCanvas.h"
 #include "IContextFactory.h"
+#include "RecordingCanvas.h"
 #include "RenderNode.h"
 #include "SkTypes.h"
 #include "gui/BufferQueue.h"
@@ -88,9 +89,11 @@
         mProxy->setup(mSize.width(), mSize.height(), 800.0f,
                              255 * 0.075f, 255 * 0.15f);
         mProxy->setLightCenter(lightVector);
-        mCanvas.reset(new
-            android::uirenderer::DisplayListCanvas(mSize.width(),
-                                                   mSize.height()));
+#if HWUI_NEW_OPS
+        mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height()));
+#else
+        mCanvas.reset(new android::uirenderer::DisplayListCanvas(mSize.width(), mSize.height()));
+#endif
     }
 
     SkCanvas* prepareToDraw() {
@@ -168,7 +171,11 @@
 
     std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
     std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
+#if HWUI_NEW_OPS
+    std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas;
+#else
     std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
+#endif
     android::sp<android::IGraphicBufferProducer> mProducer;
     android::sp<android::IGraphicBufferConsumer> mConsumer;
     android::sp<android::CpuConsumer> mCpuConsumer;
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index c10d675..606447b5 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -223,10 +223,19 @@
     @SystemApi
     public final static int FLAG_BYPASS_MUTE = 0x1 << 7;
 
+    /**
+     * @hide
+     * Flag requesting a low latency path.
+     * When using this flag, the sample rate must match the native sample rate
+     * of the device. Effects processing is also unavailable.
+     */
+    public final static int FLAG_LOW_LATENCY = 0x1 << 8;
+
     private final static int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO |
             FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY |
-            FLAG_BYPASS_MUTE;
-    private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED | FLAG_HW_AV_SYNC;
+            FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY;
+    private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
+            FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
 
     private int mUsage = USAGE_UNKNOWN;
     private int mContentType = CONTENT_TYPE_UNKNOWN;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f932b9e..5ad6b08 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -51,6 +51,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -176,6 +177,16 @@
         "android.media.MASTER_MUTE_CHANGED_ACTION";
 
     /**
+     * @hide Broadcast intent when the master mono state changes.
+     * Includes the new mono state
+     *
+     * @see #EXTRA_MASTER_MONO
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String MASTER_MONO_CHANGED_ACTION =
+        "android.media.MASTER_MONO_CHANGED_ACTION";
+
+    /**
      * The new vibrate setting for a particular type.
      *
      * @see #VIBRATE_SETTING_CHANGED_ACTION
@@ -254,6 +265,13 @@
         "android.media.EXTRA_STREAM_VOLUME_MUTED";
 
     /**
+     * @hide The new master mono state for the master mono changed intent.
+     * Value is boolean
+     */
+    public static final String EXTRA_MASTER_MONO =
+        "android.media.EXTRA_MASTER_MONO";
+
+    /**
      * Broadcast Action: Wired Headset plugged in or unplugged.
      *
      * You <em>cannot</em> receive this through components declared
@@ -880,6 +898,17 @@
         }
     }
 
+    /** @hide */
+    public void setMasterMono(boolean mono) {
+        IAudioService service = getService();
+        try {
+            service.setMasterMono(mono, getContext().getOpPackageName(),
+                    UserHandle.getCallingUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setMasterMono", e);
+        }
+    }
+
     /**
      * Returns the current ringtone mode.
      *
@@ -1142,6 +1171,21 @@
     }
 
     /**
+     * get master mono state.
+     *
+     * @hide
+     */
+    public boolean isMasterMono() {
+        IAudioService service = getService();
+        try {
+            return service.isMasterMono();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in isMasterMono", e);
+            return false;
+        }
+    }
+
+    /**
      * forces the stream controlled by hard volume keys
      * specifying streamType == -1 releases control to the
      * logic.
@@ -2115,36 +2159,73 @@
     }
 
     /**
-     * Handler for audio focus events coming from the audio service.
+     * Handler for events (audio focus change, recording config change) coming from the
+     * audio service.
      */
-    private final FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
-            new FocusEventHandlerDelegate();
+    private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate =
+            new ServiceEventHandlerDelegate();
 
     /**
-     * Helper class to handle the forwarding of audio focus events to the appropriate listener
+     * Event types
      */
-    private class FocusEventHandlerDelegate {
+    private final static int MSSG_FOCUS_CHANGE = 0;
+    private final static int MSSG_RECORDING_CONFIG_CHANGE = 1;
+
+    /**
+     * Helper class to handle the forwarding of audio service events to the appropriate listener
+     */
+    private class ServiceEventHandlerDelegate {
         private final Handler mHandler;
 
-        FocusEventHandlerDelegate() {
+        ServiceEventHandlerDelegate() {
             Looper looper;
             if ((looper = Looper.myLooper()) == null) {
                 looper = Looper.getMainLooper();
             }
 
             if (looper != null) {
-                // implement the event handler delegate to receive audio focus events
+                // implement the event handler delegate to receive events from audio service
                 mHandler = new Handler(looper) {
                     @Override
                     public void handleMessage(Message msg) {
-                        OnAudioFocusChangeListener listener = null;
-                        synchronized(mFocusListenerLock) {
-                            listener = findFocusListener((String)msg.obj);
-                        }
-                        if (listener != null) {
-                            Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
-                                    + msg.what + ") for " + msg.obj);
-                            listener.onAudioFocusChange(msg.what);
+                        switch (msg.what) {
+                            case MSSG_FOCUS_CHANGE:
+                                OnAudioFocusChangeListener listener = null;
+                                synchronized(mFocusListenerLock) {
+                                    listener = findFocusListener((String)msg.obj);
+                                }
+                                if (listener != null) {
+                                    Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
+                                            + msg.what + ") for " + msg.obj);
+                                    listener.onAudioFocusChange(msg.arg1);
+                                }
+                                break;
+                            case MSSG_RECORDING_CONFIG_CHANGE:
+                                // optimizing for the case of a single callback
+                                AudioRecordingCallback singleCallback = null;
+                                ArrayList<AudioRecordingCallback> multipleCallbacks = null;
+                                synchronized(mRecordCallbackLock) {
+                                    if ((mRecordCallbackList != null)
+                                            && (mRecordCallbackList.size() != 0)) {
+                                        if (mRecordCallbackList.size() == 1) {
+                                            singleCallback = mRecordCallbackList.get(0);
+                                        } else {
+                                            multipleCallbacks =
+                                                    new ArrayList<AudioRecordingCallback>(
+                                                            mRecordCallbackList);
+                                        }
+                                    }
+                                }
+                                if (singleCallback != null) {
+                                    singleCallback.onRecordConfigChanged();
+                                } else if (multipleCallbacks != null) {
+                                    for (int i=0 ; i < multipleCallbacks.size() ; i++) {
+                                        multipleCallbacks.get(i).onRecordConfigChanged();
+                                    }
+                                }
+                                break;
+                            default:
+                                Log.e(TAG, "Unknown event " + msg.what);
                         }
                     }
                 };
@@ -2161,8 +2242,9 @@
     private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
 
         public void dispatchAudioFocusChange(int focusChange, String id) {
-            Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
-            mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m);
+            final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
+                    MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
+            mServiceEventHandlerDelegate.getHandler().sendMessage(m);
         }
 
     };
@@ -2659,6 +2741,8 @@
     }
 
 
+    //====================================================================
+    // Audio policy
     /**
      * @hide
      * Register the given {@link AudioPolicy}.
@@ -2711,6 +2795,131 @@
     }
 
 
+    //====================================================================
+    // Recording configuration
+    /**
+     * @hide
+     * candidate for public API
+     */
+    public static abstract class AudioRecordingCallback {
+        /**
+         * @hide
+         * candidate for public API
+         */
+        public void onRecordConfigChanged() {}
+    }
+
+    /**
+     * @hide
+     * candidate for public API
+     * @param non-null callback
+     */
+    public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+        }
+        synchronized(mRecordCallbackLock) {
+            // lazy initialization of the list of recording callbacks
+            if (mRecordCallbackList == null) {
+                mRecordCallbackList = new ArrayList<AudioRecordingCallback>();
+            }
+            final int oldCbCount = mRecordCallbackList.size();
+            if (!mRecordCallbackList.contains(cb)) {
+                mRecordCallbackList.add(cb);
+                final int newCbCount = mRecordCallbackList.size();
+                if ((oldCbCount == 0) && (newCbCount > 0)) {
+                    // register binder for callbacks
+                    final IAudioService service = getService();
+                    try {
+                        service.registerRecordingCallback(mRecCb);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Dead object in registerRecordingCallback", e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously"
+                        + "registered callback");
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * candidate for public API
+     * @param non-null callback
+     */
+    public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+        if (cb == null) {
+            throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+        }
+        synchronized(mRecordCallbackLock) {
+            if (mRecordCallbackList == null) {
+                return;
+            }
+            final int oldCbCount = mRecordCallbackList.size();
+            if (mRecordCallbackList.remove(cb)) {
+                final int newCbCount = mRecordCallbackList.size();
+                if ((oldCbCount > 0) && (newCbCount == 0)) {
+                    // unregister binder for callbacks
+                    final IAudioService service = getService();
+                    try {
+                        service.unregisterRecordingCallback(mRecCb);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Dead object in unregisterRecordingCallback", e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "attempt to call unregisterAudioRecordingCallback() on a callback"
+                        + " already unregistered or never registered");
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * candidate for public API
+     * @return a non-null array of recording configurations. An array of length 0 indicates there is
+     *     no recording active when queried.
+     */
+    public @NonNull AudioRecordConfiguration[] getActiveRecordConfigurations() {
+        final IAudioService service = getService();
+        try {
+            return service.getActiveRecordConfigurations();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to retrieve active record configurations", e);
+            return null;
+        }
+    }
+
+    /**
+     * constants for the recording events, to keep in sync
+     * with frameworks/av/include/media/AudioPolicy.h
+     */
+    /** @hide */
+    public final static int RECORD_CONFIG_EVENT_START = 1;
+    /** @hide */
+    public final static int RECORD_CONFIG_EVENT_STOP = 0;
+
+    /**
+     * All operations on this list are sync'd on mRecordCallbackLock.
+     * List is lazy-initialized in {@link #registerAudioRecordingCallback(AudioRecordingCallback)}.
+     * List can be null.
+     */
+    private List<AudioRecordingCallback> mRecordCallbackList;
+    private final Object mRecordCallbackLock = new Object();
+
+    private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
+
+        public void dispatchRecordingConfigChange() {
+            final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
+                    MSSG_RECORDING_CONFIG_CHANGE/*what*/);
+            mServiceEventHandlerDelegate.getHandler().sendMessage(m);
+        }
+
+    };
+
+    //=====================================================================
+
     /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 974b62e..7c21893 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -51,7 +51,7 @@
  * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
  * the total recording buffer size.
  */
-public class AudioRecord
+public class AudioRecord implements AudioRouting
 {
     //---------------------------------------------------------
     // Constants
@@ -392,6 +392,20 @@
     }
 
     /**
+     * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
+     * the AudioRecordRoutingProxy subclass.
+     * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
+     * (associated with an OpenSL ES recorder).
+     */
+    /*package*/ AudioRecord(long nativeRecordInJavaObj) {
+        mNativeRecorderInJavaObj = nativeRecordInJavaObj;
+
+        // other initialization here...
+
+        mState = STATE_INITIALIZED;
+    }
+
+    /**
      * Builder class for {@link AudioRecord} objects.
      * Use this class to configure and create an <code>AudioRecord</code> instance. By setting the
      * recording source and audio format parameters, you indicate which of
@@ -1221,23 +1235,6 @@
         return native_set_marker_pos(markerInFrames);
     }
 
-
-    //--------------------------------------------------------------------------
-    // (Re)Routing Info
-    //--------------------
-    /**
-     * Defines the interface by which applications can receive notifications of routing
-     * changes for the associated {@link AudioRecord}.
-     */
-    public interface OnRoutingChangedListener {
-        /**
-         * Called when the routing of an AudioRecord changes from either and explicit or
-         * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
-         * device.
-         */
-        public void onRoutingChanged(AudioRecord audioRecord);
-    }
-
     /**
      * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
      * Note: The query is only valid if the AudioRecord is currently recording. If it is not,
@@ -1258,6 +1255,89 @@
         return null;
     }
 
+    /*
+     * Call BEFORE adding a routing callback handler.
+     */
+    private void testEnableNativeRoutingCallbacks() {
+        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+            native_enableDeviceCallback();
+        }
+    }
+
+    /*
+     * Call AFTER removing a routing callback handler.
+     */
+    private void testDisableNativeRoutingCallbacks() {
+        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+            native_disableDeviceCallback();
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    // >= "N" (Re)Routing Info
+    //--------------------
+    /**
+     * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
+     * {@link AudioRecord#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
+     *      android.os.Handler)}
+     * by an app to receive (re)routing notifications.
+     */
+    private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
+    mNewRoutingChangeListeners =
+        new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+
+    /**
+     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of
+     * routing changes on this AudioRecord.
+     * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
+     * notifications of rerouting events.
+     * @param handler  Specifies the {@link Handler} object for the thread on which to execute
+     * the callback. If <code>null</code>, the {@link Handler} associated with the main
+     * {@link Looper} will be used.
+     */
+    public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+            android.os.Handler handler) {
+        if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
+            synchronized (mNewRoutingChangeListeners) {
+                testEnableNativeRoutingCallbacks();
+                mNewRoutingChangeListeners.put(
+                    listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
+                            handler != null ? handler : new Handler(mInitializationLooper)));
+            }
+        }
+    }
+
+    /**
+     * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
+    * to receive rerouting notifications.
+    * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
+    * to remove.
+    */
+    public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
+        synchronized (mNewRoutingChangeListeners) {
+            if (mNewRoutingChangeListeners.containsKey(listener)) {
+                mNewRoutingChangeListeners.remove(listener);
+                testDisableNativeRoutingCallbacks();
+            }
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    // Marshmallow (Re)Routing Info
+    //--------------------
+    /**
+     * Defines the interface by which applications can receive notifications of routing
+     * changes for the associated {@link AudioRecord}.
+     */
+    public interface OnRoutingChangedListener {
+        /**
+         * Called when the routing of an AudioRecord changes from either and explicit or
+         * policy rerouting. Use {@link #getRoutedDevice()} to retrieve the newly routed-from
+         * device.
+         */
+        public void onRoutingChanged(AudioRecord audioRecord);
+    }
+
     /**
      * The list of AudioRecord.OnRoutingChangedListener interface added (with
      * {@link AudioRecord#addOnRoutingChangedListener(OnRoutingChangedListener,android.os.Handler)}
@@ -1276,13 +1356,12 @@
      * the callback. If <code>null</code>, the {@link Handler} associated with the main
      * {@link Looper} will be used.
      */
+    @Deprecated
     public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
             android.os.Handler handler) {
         if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
             synchronized (mRoutingChangeListeners) {
-                if (mRoutingChangeListeners.size() == 0) {
-                    native_enableDeviceCallback();
-                }
+                testEnableNativeRoutingCallbacks();
                 mRoutingChangeListeners.put(
                     listener, new NativeRoutingEventHandlerDelegate(this, listener,
                             handler != null ? handler : new Handler(mInitializationLooper)));
@@ -1291,22 +1370,73 @@
     }
 
     /**
-     * Removes an {@link OnRoutingChangedListener} which has been previously added
+      * Removes an {@link OnRoutingChangedListener} which has been previously added
      * to receive rerouting notifications.
      * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
      */
+    @Deprecated
     public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
         synchronized (mRoutingChangeListeners) {
             if (mRoutingChangeListeners.containsKey(listener)) {
                 mRoutingChangeListeners.remove(listener);
-                if (mRoutingChangeListeners.size() == 0) {
-                    native_disableDeviceCallback();
-                }
+                testDisableNativeRoutingCallbacks();
             }
         }
     }
 
     /**
+     * >= "N" Routing
+     * Helper class to handle the forwarding of native events to the appropriate listener
+     * (potentially) handled in a different thread
+     */
+    private class NativeNewRoutingEventHandlerDelegate {
+        private final Handler mHandler;
+
+        NativeNewRoutingEventHandlerDelegate(final AudioRecord record,
+                                   final AudioRouting.OnRoutingChangedListener listener,
+                                   Handler handler) {
+            // find the looper for our new event handler
+            Looper looper;
+            if (handler != null) {
+                looper = handler.getLooper();
+            } else {
+                // no given handler, use the looper the AudioRecord was created in
+                looper = mInitializationLooper;
+            }
+
+            // construct the event handler with this looper
+            if (looper != null) {
+                // implement the event handler delegate
+                mHandler = new Handler(looper) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        if (record == null) {
+                            return;
+                        }
+                        switch(msg.what) {
+                        case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
+                            if (listener != null) {
+                                listener.onRoutingChanged(record);
+                            }
+                            break;
+                        default:
+                            loge("Unknown native event type: " + msg.what);
+                            break;
+                        }
+                    }
+                };
+            } else {
+                mHandler = null;
+            }
+        }
+
+        Handler getHandler() {
+            return mHandler;
+        }
+    }
+
+    /**
+     * Marshmallow Routing
      * Helper class to handle the forwarding of native events to the appropriate listener
      * (potentially) handled in a different thread
      */
@@ -1355,21 +1485,34 @@
             return mHandler;
         }
     }
+
     /**
      * Sends device list change notification to all listeners.
      */
     private void broadcastRoutingChange() {
+        AudioManager.resetAudioPortGeneration();
+        // Marshmallow Routing
         Collection<NativeRoutingEventHandlerDelegate> values;
         synchronized (mRoutingChangeListeners) {
             values = mRoutingChangeListeners.values();
         }
-        AudioManager.resetAudioPortGeneration();
         for(NativeRoutingEventHandlerDelegate delegate : values) {
             Handler handler = delegate.getHandler();
             if (handler != null) {
                 handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
             }
         }
+        // >= "N" Routing
+        Collection<NativeNewRoutingEventHandlerDelegate> newValues;
+        synchronized (mNewRoutingChangeListeners) {
+            newValues = mNewRoutingChangeListeners.values();
+        }
+        for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
+            Handler handler = delegate.getHandler();
+            if (handler != null) {
+                handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/os/PersistableBundle.aidl b/media/java/android/media/AudioRecordConfiguration.aidl
similarity index 78%
rename from core/java/android/os/PersistableBundle.aidl
rename to media/java/android/media/AudioRecordConfiguration.aidl
index 94e8607..afe912b 100644
--- a/core/java/android/os/PersistableBundle.aidl
+++ b/media/java/android/media/AudioRecordConfiguration.aidl
@@ -1,6 +1,4 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
+/* Copyright 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.
@@ -15,6 +13,6 @@
 ** limitations under the License.
 */
 
-package android.os;
+package android.media;
 
-parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
+parcelable AudioRecordConfiguration;
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
new file mode 100644
index 0000000..aefe692
--- /dev/null
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -0,0 +1,102 @@
+/*
+ * 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.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ * Candidate for public API, see AudioManager.getActiveRecordConfiguration()
+ *
+ */
+public class AudioRecordConfiguration implements Parcelable {
+
+    private final int mSessionId;
+
+    private final int mClientSource;
+
+    /**
+     * @hide
+     */
+    public AudioRecordConfiguration(int session, int source) {
+        mSessionId = session;
+        mClientSource = source;
+    }
+
+    /**
+     * @return one of AudioSource.MIC, AudioSource.VOICE_UPLINK,
+     *       AudioSource.VOICE_DOWNLINK, AudioSource.VOICE_CALL,
+     *       AudioSource.CAMCORDER, AudioSource.VOICE_RECOGNITION,
+     *       AudioSource.VOICE_COMMUNICATION.
+     */
+    public int getClientAudioSource() { return mClientSource; }
+
+    /**
+     * @return the session number of the recorder.
+     */
+    public int getAudioSessionId() { return mSessionId; }
+
+
+    public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
+            = new Parcelable.Creator<AudioRecordConfiguration>() {
+        /**
+         * Rebuilds an AudioRecordConfiguration previously stored with writeToParcel().
+         * @param p Parcel object to read the AudioRecordConfiguration from
+         * @return a new AudioRecordConfiguration created from the data in the parcel
+         */
+        public AudioRecordConfiguration createFromParcel(Parcel p) {
+            return new AudioRecordConfiguration(p);
+        }
+        public AudioRecordConfiguration[] newArray(int size) {
+            return new AudioRecordConfiguration[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSessionId, mClientSource);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSessionId);
+        dest.writeInt(mClientSource);
+    }
+
+    private AudioRecordConfiguration(Parcel in) {
+        mSessionId = in.readInt();
+        mClientSource = in.readInt();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
+
+        final AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+         return ((mSessionId == that.mSessionId)
+                 && (mClientSource == that.mClientSource));
+    }
+}
\ No newline at end of file
diff --git a/media/java/android/media/AudioRecordRoutingProxy.java b/media/java/android/media/AudioRecordRoutingProxy.java
new file mode 100644
index 0000000..b0c19e4
--- /dev/null
+++ b/media/java/android/media/AudioRecordRoutingProxy.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * An AudioRecord connected to a native (C/C++) which allows access only to routing methods.
+ */
+class AudioRecordRoutingProxy extends AudioRecord {
+    /**
+     * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
+     * the AudioRecordRoutingProxy subclass.
+     * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
+     * (associated with an OpenSL ES recorder).
+     */
+    public AudioRecordRoutingProxy(long nativeRecordInJavaObj) {
+        super(nativeRecordInJavaObj);
+    }
+}
diff --git a/media/java/android/media/AudioRouting.java b/media/java/android/media/AudioRouting.java
new file mode 100644
index 0000000..2161cf3
--- /dev/null
+++ b/media/java/android/media/AudioRouting.java
@@ -0,0 +1,70 @@
+/*
+ * 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.media;
+
+import android.os.Handler;
+import android.os.Looper;
+
+/**
+ * AudioRouting defines an interface for controlling routing and routing notifications in
+ * AudioTrack and AudioRecord objects.
+ */
+public interface AudioRouting {
+    /**
+     * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
+     * the output/input to/from.
+     * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
+     *  If deviceInfo is null, default routing is restored.
+     * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
+     * does not correspond to a valid audio device.
+     */
+    public boolean setPreferredDevice(AudioDeviceInfo deviceInfo);
+
+    /**
+     * Returns the selected output/input specified by {@link #setPreferredDevice}. Note that this
+     * is not guaranteed to correspond to the actual device being used for playback/recording.
+     */
+    public AudioDeviceInfo getPreferredDevice();
+
+    /**
+     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
+     * changes on this AudioTrack/AudioRecord.
+     * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
+     * notifications of rerouting events.
+     * @param handler  Specifies the {@link Handler} object for the thread on which to execute
+     * the callback. If <code>null</code>, the {@link Handler} associated with the main
+     * {@link Looper} will be used.
+     */
+    public void addOnRoutingListener(OnRoutingChangedListener listener,
+            Handler handler);
+
+    /**
+     * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
+     * to receive rerouting notifications.
+     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
+     * to remove.
+     */
+    public void removeOnRoutingListener(OnRoutingChangedListener listener);
+
+    /**
+     * Defines the interface by which applications can receive notifications of routing
+     * changes for the associated {@link AudioRouting}.
+     */
+    public interface OnRoutingChangedListener {
+        public void onRoutingChanged(AudioRouting router);
+    }
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index c59d1c7..aa0d78d 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -227,7 +227,7 @@
     }
 
     /**
-     * Handles events for the audio policy manager about dynamic audio policies
+     * Handles events from the audio policy manager about dynamic audio policies
      * @see android.media.audiopolicy.AudioPolicy
      */
     public interface DynamicPolicyCallback
@@ -267,6 +267,33 @@
         }
     }
 
+    /**
+     * Handles events from the audio policy manager about recording events
+     * @see android.media.AudioManager.AudioRecordingCallback
+     */
+    public interface AudioRecordingCallback
+    {
+        void onRecordingConfigurationChanged(int event, int session, int source);
+    }
+
+    private static AudioRecordingCallback sRecordingCallback;
+
+    public static void setRecordingCallback(AudioRecordingCallback cb) {
+        synchronized (AudioSystem.class) {
+            sRecordingCallback = cb;
+            native_register_recording_callback();
+        }
+    }
+
+    private static void recordingCallbackFromNative(int event, int session, int source) {
+        AudioRecordingCallback cb = null;
+        synchronized (AudioSystem.class) {
+            cb = sRecordingCallback;
+        }
+        if (cb != null) {
+            cb.onRecordingConfigurationChanged(event, session, source);
+        }
+    }
 
     /*
      * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
@@ -624,6 +651,11 @@
     public static native boolean getMasterMute();
     public static native int getDevicesForStream(int stream);
 
+    /** @hide returns true if master mono is enabled. */
+    public static native boolean getMasterMono();
+    /** @hide enables or disables the master mono mode. */
+    public static native int setMasterMono(boolean mono);
+
     // helpers for android.media.AudioManager.getProperty(), see description there for meaning
     public static native int getPrimaryOutputSamplingRate();
     public static native int getPrimaryOutputFrameCount();
@@ -641,6 +673,8 @@
 
     // declare this instance as having a dynamic policy callback handler
     private static native final void native_register_dynamic_policy_callback();
+    // declare this instance as having a recording configuration update callback handler
+    private static native final void native_register_recording_callback();
 
     // must be kept in sync with value in include/system/audio.h
     public static final int AUDIO_HW_SYNC_INVALID = 0;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index bb4f7d9..c110ce8 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -78,7 +78,7 @@
  *
  * AudioTrack is not final and thus permits subclasses, but such use is not recommended.
  */
-public class AudioTrack
+public class AudioTrack implements AudioRouting
 {
     //---------------------------------------------------------
     // Constants
@@ -97,9 +97,10 @@
     /** Maximum value for sample rate */
     private static final int SAMPLE_RATE_HZ_MAX = 192000;
 
-    // FCC_8
-    /** Maximum value for AudioTrack channel count */
-    private static final int CHANNEL_COUNT_MAX = 8;
+    /** Maximum value for AudioTrack channel count
+     * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+     */
+    public static final int CHANNEL_COUNT_MAX = native_get_FCC_8();
 
     /** indicates AudioTrack state is stopped */
     public static final int PLAYSTATE_STOPPED = 1;  // matches SL_PLAYSTATE_STOPPED
@@ -317,10 +318,11 @@
     // Used exclusively by native code
     //--------------------
     /**
+     * @hide
      * Accessed by native methods: provides access to C++ AudioTrack object.
      */
     @SuppressWarnings("unused")
-    private long mNativeTrackInJavaObj;
+    protected long mNativeTrackInJavaObj;
     /**
      * Accessed by native methods: provides access to the JNI data (i.e. resources used by
      * the native AudioTrack object, but not stored in it).
@@ -523,6 +525,31 @@
     }
 
     /**
+     * A constructor which explicitly connects a Native (C++) AudioTrack. For use by
+     * the AudioTrackRoutingProxy subclass.
+     * @param nativeTrackInJavaObj a C/C++ pointer to a native AudioTrack
+     * (associated with an OpenSL ES player).
+     */
+    /*package*/ AudioTrack(long nativeTrackInJavaObj) {
+        mNativeTrackInJavaObj = nativeTrackInJavaObj;
+
+        // "final"s
+        mAttributes = null;
+        mAppOps = null;
+
+        // remember which looper is associated with the AudioTrack instantiation
+        Looper looper;
+        if ((looper = Looper.myLooper()) == null) {
+            looper = Looper.getMainLooper();
+        }
+        mInitializationLooper = looper;
+
+        // other initialization...
+
+        mState = STATE_INITIALIZED;
+    }
+
+    /**
      * Builder class for {@link AudioTrack} objects.
      * Use this class to configure and create an <code>AudioTrack</code> instance. By setting audio
      * attributes and audio format parameters, you indicate which of those vary from the default
@@ -2246,22 +2273,6 @@
         }
     }
 
-    //--------------------------------------------------------------------------
-    // (Re)Routing Info
-    //--------------------
-    /**
-     * Defines the interface by which applications can receive notifications of routing
-     * changes for the associated {@link AudioTrack}.
-     */
-    public interface OnRoutingChangedListener {
-        /**
-         * Called when the routing of an AudioTrack changes from either and explicit or
-         * policy rerouting.  Use {@link #getRoutedDevice()} to retrieve the newly routed-to
-         * device.
-         */
-        public void onRoutingChanged(AudioTrack audioTrack);
-    }
-
     /**
      * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioTrack.
      * Note: The query is only valid if the AudioTrack is currently playing. If it is not,
@@ -2282,6 +2293,89 @@
         return null;
     }
 
+    /*
+     * Call BEFORE adding a routing callback handler.
+     */
+    private void testEnableNativeRoutingCallbacks() {
+        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+            native_enableDeviceCallback();
+        }
+    }
+
+    /*
+     * Call AFTER removing a routing callback handler.
+     */
+    private void testDisableNativeRoutingCallbacks() {
+        if (mRoutingChangeListeners.size() == 0 && mNewRoutingChangeListeners.size() == 0) {
+            native_disableDeviceCallback();
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    // >= "N" (Re)Routing Info
+    //--------------------
+    /**
+     * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
+     * {@link AudioTrack#addOnRoutingListener(AudioRouting.OnRoutingChangedListener,
+     *          android.os.Handler)}
+     * by an app to receive (re)routing notifications.
+     */
+   private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>
+    mNewRoutingChangeListeners =
+        new ArrayMap<AudioRouting.OnRoutingChangedListener, NativeNewRoutingEventHandlerDelegate>();
+
+   /**
+    * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
+    * changes on this AudioTrack.
+    * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
+    * notifications of rerouting events.
+    * @param handler  Specifies the {@link Handler} object for the thread on which to execute
+    * the callback. If <code>null</code>, the {@link Handler} associated with the main
+    * {@link Looper} will be used.
+    */
+    public void addOnRoutingListener(AudioRouting.OnRoutingChangedListener listener,
+            Handler handler) {
+        if (listener != null && !mNewRoutingChangeListeners.containsKey(listener)) {
+            synchronized (mNewRoutingChangeListeners) {
+                testEnableNativeRoutingCallbacks();
+                mNewRoutingChangeListeners.put(
+                    listener, new NativeNewRoutingEventHandlerDelegate(this, listener,
+                            handler != null ? handler : new Handler(mInitializationLooper)));
+            }
+        }
+    }
+
+    /**
+     * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
+     * to receive rerouting notifications.
+     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
+     * to remove.
+     */
+    public void removeOnRoutingListener(AudioRouting.OnRoutingChangedListener listener) {
+        if (mNewRoutingChangeListeners.containsKey(listener)) {
+            mNewRoutingChangeListeners.remove(listener);
+        }
+        testDisableNativeRoutingCallbacks();
+    }
+
+    //--------------------------------------------------------------------------
+    // Marshmallow (Re)Routing Info
+    //--------------------
+    /**
+     * Defines the interface by which applications can receive notifications of routing
+     * changes for the associated {@link AudioTrack}.
+     */
+    @Deprecated
+    public interface OnRoutingChangedListener {
+        /**
+         * Called when the routing of an AudioTrack changes from either and explicit or
+         * policy rerouting.  Use {@link #getRoutedDevice()} to retrieve the newly routed-to
+         * device.
+         */
+        @Deprecated
+        public void onRoutingChanged(AudioTrack audioTrack);
+    }
+
     /**
      * The list of AudioTrack.OnRoutingChangedListener interfaces added (with
      * {@link AudioTrack#addOnRoutingChangedListener(OnRoutingChangedListener, android.os.Handler)}
@@ -2300,13 +2394,12 @@
      * the callback. If <code>null</code>, the {@link Handler} associated with the main
      * {@link Looper} will be used.
      */
+    @Deprecated
     public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
             android.os.Handler handler) {
         if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
             synchronized (mRoutingChangeListeners) {
-                if (mRoutingChangeListeners.size() == 0) {
-                    native_enableDeviceCallback();
-                }
+                testEnableNativeRoutingCallbacks();
                 mRoutingChangeListeners.put(
                     listener, new NativeRoutingEventHandlerDelegate(this, listener,
                             handler != null ? handler : new Handler(mInitializationLooper)));
@@ -2319,14 +2412,13 @@
      * to receive rerouting notifications.
      * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
      */
+    @Deprecated
     public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
         synchronized (mRoutingChangeListeners) {
             if (mRoutingChangeListeners.containsKey(listener)) {
                 mRoutingChangeListeners.remove(listener);
             }
-            if (mRoutingChangeListeners.size() == 0) {
-                native_disableDeviceCallback();
-            }
+            testDisableNativeRoutingCallbacks();
         }
     }
 
@@ -2334,17 +2426,30 @@
      * Sends device list change notification to all listeners.
      */
     private void broadcastRoutingChange() {
+        AudioManager.resetAudioPortGeneration();
+
+        // Marshmallow Routing
         Collection<NativeRoutingEventHandlerDelegate> values;
         synchronized (mRoutingChangeListeners) {
             values = mRoutingChangeListeners.values();
         }
-        AudioManager.resetAudioPortGeneration();
         for(NativeRoutingEventHandlerDelegate delegate : values) {
             Handler handler = delegate.getHandler();
             if (handler != null) {
                 handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
             }
         }
+        // >= "N" Routing
+        Collection<NativeNewRoutingEventHandlerDelegate> newValues;
+        synchronized (mNewRoutingChangeListeners) {
+            newValues = mNewRoutingChangeListeners.values();
+        }
+        for(NativeNewRoutingEventHandlerDelegate delegate : newValues) {
+            Handler handler = delegate.getHandler();
+            if (handler != null) {
+                handler.sendEmptyMessage(AudioSystem.NATIVE_EVENT_ROUTING_CHANGE);
+            }
+        }
     }
 
     //---------------------------------------------------------
@@ -2427,6 +2532,7 @@
     }
 
     /**
+     * Marshmallow Routing API.
      * Helper class to handle the forwarding of native events to the appropriate listener
      * (potentially) handled in a different thread
      */
@@ -2476,6 +2582,57 @@
         }
     }
 
+    /**
+     * Marshmallow Routing API.
+     * Helper class to handle the forwarding of native events to the appropriate listener
+     * (potentially) handled in a different thread
+     */
+    private class NativeNewRoutingEventHandlerDelegate {
+        private final Handler mHandler;
+
+        NativeNewRoutingEventHandlerDelegate(final AudioTrack track,
+                                   final AudioRouting.OnRoutingChangedListener listener,
+                                   Handler handler) {
+            // find the looper for our new event handler
+            Looper looper;
+            if (handler != null) {
+                looper = handler.getLooper();
+            } else {
+                // no given handler, use the looper the AudioTrack was created in
+                looper = mInitializationLooper;
+            }
+
+            // construct the event handler with this looper
+            if (looper != null) {
+                // implement the event handler delegate
+                mHandler = new Handler(looper) {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        if (track == null) {
+                            return;
+                        }
+                        switch(msg.what) {
+                        case AudioSystem.NATIVE_EVENT_ROUTING_CHANGE:
+                            if (listener != null) {
+                                listener.onRoutingChanged(track);
+                            }
+                            break;
+                        default:
+                            loge("Unknown native event type: " + msg.what);
+                            break;
+                        }
+                    }
+                };
+            } else {
+                mHandler = null;
+            }
+        }
+
+        Handler getHandler() {
+            return mHandler;
+        }
+    }
+
     //---------------------------------------------------------
     // Java methods called from the native side
     //--------------------
@@ -2583,6 +2740,7 @@
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback();
     private native final void native_disableDeviceCallback();
+    static private native int native_get_FCC_8();
 
     //---------------------------------------------------------
     // Utility methods
diff --git a/media/java/android/media/AudioTrackRoutingProxy.java b/media/java/android/media/AudioTrackRoutingProxy.java
new file mode 100644
index 0000000..9b97ae9
--- /dev/null
+++ b/media/java/android/media/AudioTrackRoutingProxy.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * An AudioTrack connected to a native (C/C++) which allows access only to routing methods.
+ */
+class AudioTrackRoutingProxy extends AudioTrack {
+    /**
+     * A constructor which explicitly connects a Native (C++) AudioTrack. For use by
+     * the AudioTrackRoutingProxy subclass.
+     * @param nativeTrackInJavaObj a C/C++ pointer to a native AudioTrack
+     * (associated with an OpenSL ES player).
+     */
+    public AudioTrackRoutingProxy(long nativeTrackInJavaObj) {
+        super(nativeTrackInJavaObj);
+    }
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 693a519..abe92c7 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,9 +20,11 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.media.AudioAttributes;
+import android.media.AudioRecordConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
+import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
 import android.media.IVolumeController;
 import android.media.Rating;
@@ -52,6 +54,10 @@
 
     void setMasterMute(boolean mute, int flags, String callingPackage, int userId);
 
+    boolean isMasterMono();
+
+    void setMasterMono(boolean mute, String callingPackage, int userId);
+
     int getStreamVolume(int streamType);
 
     int getStreamMinVolume(int streamType);
@@ -157,4 +163,10 @@
     int setFocusPropertiesForPolicy(int duckingBehavior, in IAudioPolicyCallback pcb);
 
     void setVolumePolicy(in VolumePolicy policy);
+
+    void registerRecordingCallback(in IRecordingConfigDispatcher rcdb);
+
+    oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
+
+    AudioRecordConfiguration[] getActiveRecordConfigurations();
 }
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/media/java/android/media/IMediaResourceMonitor.aidl
similarity index 64%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to media/java/android/media/IMediaResourceMonitor.aidl
index 805fe2c..7b4bc39 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/media/java/android/media/IMediaResourceMonitor.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * Copyright 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
+ *      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,
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.media;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+/** {@hide} */
+interface IMediaResourceMonitor
+{
+    oneway void notifyResourceGranted(in int pid, String type, String subType, long value);
+}
+
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java b/media/java/android/media/IRecordingConfigDispatcher.aidl
similarity index 64%
rename from tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java
rename to media/java/android/media/IRecordingConfigDispatcher.aidl
index a9e1777..a5eb8b9f 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.perftest;
-import android.renderscript.*;
-import android.content.res.Resources;
+package android.media;
 
-interface RsBenchBaseTest {
-    boolean init(RenderScriptGL rs, Resources res);
+/**
+ * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
+ *
+ * {@hide}
+ */
+oneway interface IRecordingConfigDispatcher {
 
-    ScriptField_TestScripts_s.Item[] getTests();
-    String[] getTestNames();
+    void dispatchRecordingConfigChange();
+
 }
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 478fd99..f1f8437 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2175,8 +2175,6 @@
             int offset, int size, long presentationTimeUs, int flags)
         throws CryptoException;
 
-    // The following mode constants MUST stay in sync with their equivalents
-    // in media/hardware/CryptoAPI.h !
     public static final int CRYPTO_MODE_UNENCRYPTED = 0;
     public static final int CRYPTO_MODE_AES_CTR     = 1;
     public static final int CRYPTO_MODE_AES_CBC     = 2;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index e6bc8f1..0fba992 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -907,7 +907,7 @@
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
                 sampleRateRange = Range.create(1, 96000);
                 bitRates = Range.create(1, 10000000);
-                maxChannels = 8;
+                maxChannels = AudioTrack.CHANNEL_COUNT_MAX;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
                 sampleRateRange = Range.create(1, 655350);
                 // lossless codec, so bitrate is ignored
@@ -1735,8 +1735,7 @@
             CodecProfileLevel[] profileLevels = mParent.profileLevels;
             String mime = mParent.getMimeType();
 
-            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC) ||
-                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_AVC)) {
+            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) {
                 maxBlocks = 99;
                 maxBlocksPerSecond = 1485;
                 maxBps = 64000;
@@ -2090,8 +2089,7 @@
                 applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE,
                         maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
                         1 /* widthAlignment */, 1 /* heightAlignment */);
-            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) ||
-                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_HEVC)) {
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
                 maxBlocks = 36864;
                 maxBlocksPerSecond = maxBlocks * 15;
                 maxBps = 128000;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index a102e51..b2fa0ac 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -92,8 +92,6 @@
     public static final String MIMETYPE_VIDEO_H263 = "video/3gpp";
     public static final String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
     public static final String MIMETYPE_VIDEO_RAW = "video/raw";
-    public static final String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
-    public static final String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
 
     public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
     public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
diff --git a/media/java/android/media/MediaInserter.java b/media/java/android/media/MediaInserter.java
index 41b369d..dd06921 100644
--- a/media/java/android/media/MediaInserter.java
+++ b/media/java/android/media/MediaInserter.java
@@ -16,8 +16,8 @@
 
 package android.media;
 
+import android.content.ContentProviderClient;
 import android.content.ContentValues;
-import android.content.IContentProvider;
 import android.net.Uri;
 import android.os.RemoteException;
 
@@ -37,13 +37,11 @@
     private final HashMap<Uri, List<ContentValues>> mPriorityRowMap =
             new HashMap<Uri, List<ContentValues>>();
 
-    private final IContentProvider mProvider;
-    private final String mPackageName;
+    private final ContentProviderClient mProvider;
     private final int mBufferSizePerUri;
 
-    public MediaInserter(IContentProvider provider, String packageName, int bufferSizePerUri) {
+    public MediaInserter(ContentProviderClient provider, int bufferSizePerUri) {
         mProvider = provider;
-        mPackageName = packageName;
         mBufferSizePerUri = bufferSizePerUri;
     }
 
@@ -90,7 +88,7 @@
         if (!list.isEmpty()) {
             ContentValues[] valuesArray = new ContentValues[list.size()];
             valuesArray = list.toArray(valuesArray);
-            mProvider.bulkInsert(mPackageName, tableUri, valuesArray);
+            mProvider.bulkInsert(tableUri, valuesArray);
             list.clear();
         }
     }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 9ea6722..dfe024a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -16,14 +16,11 @@
 
 package android.media;
 
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.IContentProvider;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.drm.DrmManagerClient;
@@ -41,6 +38,7 @@
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
 import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.sax.Element;
 import android.sax.ElementListener;
 import android.sax.RootElement;
@@ -50,6 +48,12 @@
 import android.util.Log;
 import android.util.Xml;
 
+import dalvik.system.CloseGuard;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -61,6 +65,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Internal service helper that no-one should use directly.
@@ -107,8 +112,7 @@
  *
  * {@hide}
  */
-public class MediaScanner
-{
+public class MediaScanner implements AutoCloseable {
     static {
         System.loadLibrary("media_jni");
         native_init();
@@ -302,21 +306,23 @@
     };
 
     private long mNativeContext;
-    private Context mContext;
-    private String mPackageName;
-    private IContentProvider mMediaProvider;
-    private Uri mAudioUri;
-    private Uri mVideoUri;
-    private Uri mImagesUri;
-    private Uri mThumbsUri;
-    private Uri mPlaylistsUri;
-    private Uri mFilesUri;
-    private Uri mFilesUriNoNotify;
-    private boolean mProcessPlaylists, mProcessGenres;
+    private final Context mContext;
+    private final String mPackageName;
+    private final String mVolumeName;
+    private final ContentProviderClient mMediaProvider;
+    private final Uri mAudioUri;
+    private final Uri mVideoUri;
+    private final Uri mImagesUri;
+    private final Uri mThumbsUri;
+    private final Uri mPlaylistsUri;
+    private final Uri mFilesUri;
+    private final Uri mFilesUriNoNotify;
+    private final boolean mProcessPlaylists;
+    private final boolean mProcessGenres;
     private int mMtpObjectHandle;
 
-    private final String mExternalStoragePath;
-    private final boolean mExternalIsEmulated;
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
     /** whether to use bulk inserts or individual inserts for each item */
     private static final boolean ENABLE_BULK_INSERTS = true;
@@ -324,8 +330,6 @@
     // used when scanning the image database so we know whether we have to prune
     // old thumbnail files
     private int mOriginalCount;
-    /** Whether the database had any entries in it before the scan started */
-    private boolean mWasEmptyPriorToScan = false;
     /** Whether the scanner has set a default sound for the ringer ringtone. */
     private boolean mDefaultRingtoneSet;
     /** Whether the scanner has set a default sound for the notification ringtone. */
@@ -345,10 +349,6 @@
      */
     private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
 
-    // set to true if file path comparisons should be case insensitive.
-    // this should be set when scanning files on a case insensitive file system.
-    private boolean mCaseInsensitivePaths;
-
     private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
 
     private static class FileEntry {
@@ -378,26 +378,59 @@
         int bestmatchlevel;
     }
 
-    private ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<PlaylistEntry>();
+    private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
+    private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
 
     private MediaInserter mMediaInserter;
 
-    private ArrayList<FileEntry> mPlayLists;
-
     private DrmManagerClient mDrmManagerClient = null;
 
-    public MediaScanner(Context c) {
+    public MediaScanner(Context c, String volumeName) {
         native_setup();
         mContext = c;
         mPackageName = c.getPackageName();
+        mVolumeName = volumeName;
+
         mBitmapOptions.inSampleSize = 1;
         mBitmapOptions.inJustDecodeBounds = true;
 
         setDefaultRingtoneFileNames();
 
-        mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
-        mExternalIsEmulated = Environment.isExternalStorageEmulated();
-        //mClient.testGenreNameConverter();
+        mMediaProvider = mContext.getContentResolver()
+                .acquireContentProviderClient(MediaStore.AUTHORITY);
+
+        mAudioUri = Audio.Media.getContentUri(volumeName);
+        mVideoUri = Video.Media.getContentUri(volumeName);
+        mImagesUri = Images.Media.getContentUri(volumeName);
+        mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
+        mFilesUri = Files.getContentUri(volumeName);
+        mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
+
+        if (!volumeName.equals("internal")) {
+            // we only support playlists on external media
+            mProcessPlaylists = true;
+            mProcessGenres = true;
+            mPlaylistsUri = Playlists.getContentUri(volumeName);
+        } else {
+            mProcessPlaylists = false;
+            mProcessGenres = false;
+            mPlaylistsUri = null;
+        }
+
+        final Locale locale = mContext.getResources().getConfiguration().locale;
+        if (locale != null) {
+            String language = locale.getLanguage();
+            String country = locale.getCountry();
+            if (language != null) {
+                if (country != null) {
+                    setLocale(language + "_" + country);
+                } else {
+                    setLocale(language);
+                }
+            }
+        }
+
+        mCloseGuard.open("close");
     }
 
     private void setDefaultRingtoneFileNames() {
@@ -529,12 +562,29 @@
                 FileEntry entry = beginFile(path, mimeType, lastModified,
                         fileSize, isDirectory, noMedia);
 
+                if (entry == null) {
+                    return null;
+                }
+
                 // if this file was just inserted via mtp, set the rowid to zero
                 // (even though it already exists in the database), to trigger
                 // the correct code path for updating its entry
                 if (mMtpObjectHandle != 0) {
                     entry.mRowId = 0;
                 }
+
+                if (entry.mPath != null &&
+                        ((!mDefaultNotificationSet &&
+                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
+                        || (!mDefaultRingtoneSet &&
+                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
+                        || (!mDefaultAlarmSet &&
+                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
+                    Log.w(TAG, "forcing rescan of " + entry.mPath +
+                            "since ringtone setting didn't finish");
+                    scanAlways = true;
+                }
+
                 // rescan for metadata if file was modified since last scan
                 if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
                     if (noMedia) {
@@ -914,6 +964,26 @@
             }
             Uri result = null;
             boolean needToSetSettings = false;
+            // Setting a flag in order not to use bulk insert for the file related with
+            // notifications, ringtones, and alarms, because the rowId of the inserted file is
+            // needed.
+            if (notifications && !mDefaultNotificationSet) {
+                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+                    needToSetSettings = true;
+                }
+            } else if (ringtones && !mDefaultRingtoneSet) {
+                if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
+                    needToSetSettings = true;
+                }
+            } else if (alarms && !mDefaultAlarmSet) {
+                if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+                        doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+                    needToSetSettings = true;
+                }
+            }
+
             if (rowId == 0) {
                 if (mMtpObjectHandle != 0) {
                     values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -925,28 +995,6 @@
                     }
                     values.put(Files.FileColumns.FORMAT, format);
                 }
-                // Setting a flag in order not to use bulk insert for the file related with
-                // notifications, ringtones, and alarms, because the rowId of the inserted file is
-                // needed.
-                if (mWasEmptyPriorToScan) {
-                    if (notifications && !mDefaultNotificationSet) {
-                        if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
-                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
-                            needToSetSettings = true;
-                        }
-                    } else if (ringtones && !mDefaultRingtoneSet) {
-                        if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
-                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
-                            needToSetSettings = true;
-                        }
-                    } else if (alarms && !mDefaultAlarmSet) {
-                        if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
-                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
-                            needToSetSettings = true;
-                        }
-                    }
-                }
-
                 // New file, insert it.
                 // Directories need to be inserted before the files they contain, so they
                 // get priority when bulk inserting.
@@ -956,7 +1004,7 @@
                     if (inserter != null) {
                         inserter.flushAll();
                     }
-                    result = mMediaProvider.insert(mPackageName, tableUri, values);
+                    result = mMediaProvider.insert(tableUri, values);
                 } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
                     inserter.insertwithPriority(tableUri, values);
                 } else {
@@ -988,7 +1036,7 @@
                     }
                     values.put(FileColumns.MEDIA_TYPE, mediaType);
                 }
-                mMediaProvider.update(mPackageName, result, values, null, null);
+                mMediaProvider.update(result, values, null, null);
             }
 
             if(needToSetSettings) {
@@ -1016,14 +1064,18 @@
 
         private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
 
-            String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
-                    settingName);
+            if(wasSettingAlreadySet(settingName)) {
+                return;
+            }
 
+            ContentResolver cr = mContext.getContentResolver();
+            String existingSettingValue = Settings.System.getString(cr, settingName);
             if (TextUtils.isEmpty(existingSettingValue)) {
                 // Set the setting to the given URI
-                Settings.System.putString(mContext.getContentResolver(), settingName,
+                Settings.System.putString(cr, settingName,
                         ContentUris.withAppendedId(uri, rowId).toString());
             }
+            Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
         }
 
         private int getFileTypeFromDrm(String path) {
@@ -1050,16 +1102,26 @@
 
     }; // end of anonymous MediaScannerClient instance
 
+    private String settingSetIndicatorName(String base) {
+        return base + "_set";
+    }
+
+    private boolean wasSettingAlreadySet(String name) {
+        ContentResolver cr = mContext.getContentResolver();
+        String indicatorName = settingSetIndicatorName(name);
+        try {
+            return Settings.System.getInt(cr, indicatorName) != 0;
+        } catch (SettingNotFoundException e) {
+            return false;
+        }
+    }
+
     private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
         Cursor c = null;
         String where = null;
         String[] selectionArgs = null;
 
-        if (mPlayLists == null) {
-            mPlayLists = new ArrayList<FileEntry>();
-        } else {
-            mPlayLists.clear();
-        }
+        mPlayLists.clear();
 
         if (filePath != null) {
             // query for only one file
@@ -1071,14 +1133,17 @@
             selectionArgs = new String[] { "" };
         }
 
+        mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
+        mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
+        mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);
+
         // Tell the provider to not delete the file.
         // If the file is truly gone the delete is unnecessary, and we want to avoid
         // accidentally deleting files that are really there (this may happen if the
         // filesystem is mounted and unmounted while the scanner is running).
         Uri.Builder builder = mFilesUri.buildUpon();
         builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
-        MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, mPackageName,
-                builder.build());
+        MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
 
         // Build the list of files from the content provider
         try {
@@ -1089,7 +1154,6 @@
                 // with CursorWindow positioning.
                 long lastId = Long.MIN_VALUE;
                 Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
-                mWasEmptyPriorToScan = true;
 
                 while (true) {
                     selectionArgs[0] = "" + lastId;
@@ -1097,7 +1161,7 @@
                         c.close();
                         c = null;
                     }
-                    c = mMediaProvider.query(mPackageName, limitUri, FILES_PRESCAN_PROJECTION,
+                    c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
                             where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
                     if (c == null) {
                         break;
@@ -1108,7 +1172,6 @@
                     if (num == 0) {
                         break;
                     }
-                    mWasEmptyPriorToScan = false;
                     while (c.moveToNext()) {
                         long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                         String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1138,8 +1201,7 @@
                                     if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
                                         deleter.flush();
                                         String parent = new File(path).getParent();
-                                        mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL,
-                                                parent, null);
+                                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
                                     }
                                 }
                             }
@@ -1157,7 +1219,7 @@
 
         // compute original size of images
         mOriginalCount = 0;
-        c = mMediaProvider.query(mPackageName, mImagesUri, ID_PROJECTION, null, null, null, null);
+        c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
         if (c != null) {
             mOriginalCount = c.getCount();
             c.close();
@@ -1189,7 +1251,6 @@
 
         try {
             c = mMediaProvider.query(
-                    mPackageName,
                     mThumbsUri,
                     new String [] { "_data" },
                     null,
@@ -1225,13 +1286,11 @@
     static class MediaBulkDeleter {
         StringBuilder whereClause = new StringBuilder();
         ArrayList<String> whereArgs = new ArrayList<String>(100);
-        final IContentProvider mProvider;
-        final String mPackageName;
+        final ContentProviderClient mProvider;
         final Uri mBaseUri;
 
-        public MediaBulkDeleter(IContentProvider provider, String packageName, Uri baseUri) {
+        public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
             mProvider = provider;
-            mPackageName = packageName;
             mBaseUri = baseUri;
         }
 
@@ -1250,7 +1309,7 @@
             if (size > 0) {
                 String [] foo = new String [size];
                 foo = whereArgs.toArray(foo);
-                int numrows = mProvider.delete(mPackageName, mBaseUri,
+                int numrows = mProvider.delete(mBaseUri,
                         MediaStore.MediaColumns._ID + " IN (" +
                         whereClause.toString() + ")", foo);
                 //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
@@ -1260,7 +1319,7 @@
         }
     }
 
-    private void postscan(String[] directories) throws RemoteException {
+    private void postscan(final String[] directories) throws RemoteException {
 
         // handle playlists last, after we know what media files are on the storage.
         if (mProcessPlaylists) {
@@ -1271,48 +1330,26 @@
             pruneDeadThumbnailFiles();
 
         // allow GC to clean up
-        mPlayLists = null;
-        mMediaProvider = null;
+        mPlayLists.clear();
     }
 
     private void releaseResources() {
         // release the DrmManagerClient resources
         if (mDrmManagerClient != null) {
-            mDrmManagerClient.release();
+            mDrmManagerClient.close();
             mDrmManagerClient = null;
         }
     }
 
-    private void initialize(String volumeName) {
-        mMediaProvider = mContext.getContentResolver().acquireProvider("media");
-
-        mAudioUri = Audio.Media.getContentUri(volumeName);
-        mVideoUri = Video.Media.getContentUri(volumeName);
-        mImagesUri = Images.Media.getContentUri(volumeName);
-        mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
-        mFilesUri = Files.getContentUri(volumeName);
-        mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
-
-        if (!volumeName.equals("internal")) {
-            // we only support playlists on external media
-            mProcessPlaylists = true;
-            mProcessGenres = true;
-            mPlaylistsUri = Playlists.getContentUri(volumeName);
-
-            mCaseInsensitivePaths = true;
-        }
-    }
-
-    public void scanDirectories(String[] directories, String volumeName) {
+    public void scanDirectories(String[] directories) {
         try {
             long start = System.currentTimeMillis();
-            initialize(volumeName);
             prescan(null, true);
             long prescan = System.currentTimeMillis();
 
             if (ENABLE_BULK_INSERTS) {
                 // create MediaInserter for bulk inserts
-                mMediaInserter = new MediaInserter(mMediaProvider, mPackageName, 500);
+                mMediaInserter = new MediaInserter(mMediaProvider, 500);
             }
 
             for (int i = 0; i < directories.length; i++) {
@@ -1349,9 +1386,8 @@
     }
 
     // this function is used to scan a single file
-    public Uri scanSingleFile(String path, String volumeName, String mimeType) {
+    public Uri scanSingleFile(String path, String mimeType) {
         try {
-            initialize(volumeName);
             prescan(path, true);
 
             File file = new File(path);
@@ -1464,8 +1500,7 @@
         return isNoMediaFile(path);
     }
 
-    public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
-        initialize(volumeName);
+    public void scanMtpFile(String path, int objectHandle, int format) {
         MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
         int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
         File file = new File(path);
@@ -1481,7 +1516,7 @@
             values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
             try {
                 String[] whereArgs = new String[] {  Integer.toString(objectHandle) };
-                mMediaProvider.update(mPackageName, Files.getMtpObjectsUri(volumeName), values,
+                mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
                         "_id=?", whereArgs);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException in scanMtpFile", e);
@@ -1498,7 +1533,7 @@
 
                 FileEntry entry = makeEntryFor(path);
                 if (entry != null) {
-                    fileList = mMediaProvider.query(mPackageName, mFilesUri,
+                    fileList = mMediaProvider.query(mFilesUri,
                             FILES_PRESCAN_PROJECTION, null, null, null, null);
                     processPlayList(entry, fileList);
                 }
@@ -1529,7 +1564,7 @@
         try {
             where = Files.FileColumns.DATA + "=?";
             selectionArgs = new String[] { path };
-            c = mMediaProvider.query(mPackageName, mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
+            c = mMediaProvider.query(mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
                     where, selectionArgs, null, null);
             if (c.moveToFirst()) {
                 long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
@@ -1641,7 +1676,7 @@
                     values.clear();
                     values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
                     values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
-                    mMediaProvider.insert(mPackageName, playlistUri, values);
+                    mMediaProvider.insert(playlistUri, values);
                     index++;
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
@@ -1806,16 +1841,16 @@
 
         if (rowId == 0) {
             values.put(MediaStore.Audio.Playlists.DATA, path);
-            uri = mMediaProvider.insert(mPackageName, mPlaylistsUri, values);
+            uri = mMediaProvider.insert(mPlaylistsUri, values);
             rowId = ContentUris.parseId(uri);
             membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
         } else {
             uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
-            mMediaProvider.update(mPackageName, uri, values, null, null);
+            mMediaProvider.update(uri, values, null, null);
 
             // delete members of existing playlist
             membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
-            mMediaProvider.delete(mPackageName, membersUri, null, null);
+            mMediaProvider.delete(membersUri, null, null);
         }
 
         String playListDirectory = path.substring(0, lastSlash + 1);
@@ -1837,7 +1872,7 @@
         try {
             // use the files uri and projection because we need the format column,
             // but restrict the query to just audio files
-            fileList = mMediaProvider.query(mPackageName, mFilesUri, FILES_PRESCAN_PROJECTION,
+            fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
                     "media_type=2", null, null, null);
             while (iterator.hasNext()) {
                 FileEntry entry = iterator.next();
@@ -1856,7 +1891,7 @@
 
     private native void processDirectory(String path, MediaScannerClient client);
     private native void processFile(String path, String mimeType, MediaScannerClient client);
-    public native void setLocale(String locale);
+    private native void setLocale(String locale);
 
     public native byte[] extractAlbumArt(FileDescriptor fd);
 
@@ -1864,19 +1899,22 @@
     private native final void native_setup();
     private native final void native_finalize();
 
-    /**
-     * Releases resources associated with this MediaScanner object.
-     * It is considered good practice to call this method when
-     * one is done using the MediaScanner object. After this method
-     * is called, the MediaScanner object can no longer be used.
-     */
-    public void release() {
-        native_finalize();
+    @Override
+    public void close() {
+        mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
+            mMediaProvider.close();
+            native_finalize();
+        }
     }
 
     @Override
-    protected void finalize() {
-        mContext.getContentResolver().releaseProvider(mMediaProvider);
-        native_finalize();
+    protected void finalize() throws Throwable {
+        try {
+            mCloseGuard.warnIfOpen();
+            close();
+        } finally {
+            super.finalize();
+        }
     }
 }
diff --git a/media/java/android/mtp/MtpConstants.java b/media/java/android/mtp/MtpConstants.java
index d245f588..b2a5a44 100644
--- a/media/java/android/mtp/MtpConstants.java
+++ b/media/java/android/mtp/MtpConstants.java
@@ -573,4 +573,112 @@
      * Association type for objects representing file system directories.
      */
     public static final int ASSOCIATION_TYPE_GENERIC_FOLDER = 0x0001;
+
+    /** Event code for UNDEFINED event */
+    public static final int EVENT_UNDEFINED = 0x4000;
+    /** Event code for CANCEL_TRANSACTION event */
+    public static final int EVENT_CANCEL_TRANSACTION = 0x4001;
+    /** Event code for OBJECT_ADDED event */
+    public static final int EVENT_OBJECT_ADDED = 0x4002;
+    /** Event code for OBJECT_REMOVED event */
+    public static final int EVENT_OBJECT_REMOVED = 0x4003;
+    /** Event code for STORE_ADDED event */
+    public static final int EVENT_STORE_ADDED = 0x4004;
+    /** Event code for STORE_REMOVED event */
+    public static final int EVENT_STORE_REMOVED = 0x4005;
+    /** Event code for DEVICE_PROP_CHANGED event */
+    public static final int EVENT_DEVICE_PROP_CHANGED = 0x4006;
+    /** Event code for OBJECT_INFO_CHANGED event */
+    public static final int EVENT_OBJECT_INFO_CHANGED = 0x4007;
+    /** Event code for DEVICE_INFO_CHANGED event */
+    public static final int EVENT_DEVICE_INFO_CHANGED = 0x4008;
+    /** Event code for REQUEST_OBJECT_TRANSFER event */
+    public static final int EVENT_REQUEST_OBJECT_TRANSFER = 0x4009;
+    /** Event code for STORE_FULL event */
+    public static final int EVENT_STORE_FULL = 0x400A;
+    /** Event code for DEVICE_RESET event */
+    public static final int EVENT_DEVICE_RESET = 0x400B;
+    /** Event code for STORAGE_INFO_CHANGED event */
+    public static final int EVENT_STORAGE_INFO_CHANGED = 0x400C;
+    /** Event code for CAPTURE_COMPLETE event */
+    public static final int EVENT_CAPTURE_COMPLETE = 0x400D;
+    /** Event code for UNREPORTED_STATUS event */
+    public static final int EVENT_UNREPORTED_STATUS = 0x400E;
+    /** Event code for OBJECT_PROP_CHANGED event */
+    public static final int EVENT_OBJECT_PROP_CHANGED = 0xC801;
+    /** Event code for OBJECT_PROP_DESC_CHANGED event */
+    public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 0xC802;
+    /** Event code for OBJECT_REFERENCES_CHANGED event */
+    public static final int EVENT_OBJECT_REFERENCES_CHANGED = 0xC803;
+
+    /** Operation code for GetDeviceInfo */
+    public static final int OPERATION_GET_DEVICE_INFO = 0x1001;
+    /** Operation code for OpenSession */
+    public static final int OPERATION_OPEN_SESSION = 0x1002;
+    /** Operation code for CloseSession */
+    public static final int OPERATION_CLOSE_SESSION = 0x1003;
+    /** Operation code for GetStorageIDs */
+    public static final int OPERATION_GET_STORAGE_I_DS = 0x1004;
+    /** Operation code for GetStorageInfo */
+    public static final int OPERATION_GET_STORAGE_INFO = 0x1005;
+    /** Operation code for GetNumObjects */
+    public static final int OPERATION_GET_NUM_OBJECTS = 0x1006;
+    /** Operation code for GetObjectHandles */
+    public static final int OPERATION_GET_OBJECT_HANDLES = 0x1007;
+    /** Operation code for GetObjectInfo */
+    public static final int OPERATION_GET_OBJECT_INFO = 0x1008;
+    /** Operation code for GetObject */
+    public static final int OPERATION_GET_OBJECT = 0x1009;
+    /** Operation code for GetThumb */
+    public static final int OPERATION_GET_THUMB = 0x100A;
+    /** Operation code for DeleteObject */
+    public static final int OPERATION_DELETE_OBJECT = 0x100B;
+    /** Operation code for SendObjectInfo */
+    public static final int OPERATION_SEND_OBJECT_INFO = 0x100C;
+    /** Operation code for SendObject */
+    public static final int OPERATION_SEND_OBJECT = 0x100D;
+    /** Operation code for InitiateCapture */
+    public static final int OPERATION_INITIATE_CAPTURE = 0x100E;
+    /** Operation code for FormatStore */
+    public static final int OPERATION_FORMAT_STORE = 0x100F;
+    /** Operation code for ResetDevice */
+    public static final int OPERATION_RESET_DEVICE = 0x1010;
+    /** Operation code for SelfTest */
+    public static final int OPERATION_SELF_TEST = 0x1011;
+    /** Operation code for SetObjectProtection */
+    public static final int OPERATION_SET_OBJECT_PROTECTION = 0x1012;
+    /** Operation code for PowerDown */
+    public static final int OPERATION_POWER_DOWN = 0x1013;
+    /** Operation code for GetDevicePropDesc */
+    public static final int OPERATION_GET_DEVICE_PROP_DESC = 0x1014;
+    /** Operation code for GetDevicePropValue */
+    public static final int OPERATION_GET_DEVICE_PROP_VALUE = 0x1015;
+    /** Operation code for SetDevicePropValue */
+    public static final int OPERATION_SET_DEVICE_PROP_VALUE = 0x1016;
+    /** Operation code for ResetDevicePropValue */
+    public static final int OPERATION_RESET_DEVICE_PROP_VALUE = 0x1017;
+    /** Operation code for TerminateOpenCapture */
+    public static final int OPERATION_TERMINATE_OPEN_CAPTURE = 0x1018;
+    /** Operation code for MoveObject */
+    public static final int OPERATION_MOVE_OBJECT = 0x1019;
+    /** Operation code for CopyObject */
+    public static final int OPERATION_COPY_OBJECT = 0x101A;
+    /** Operation code for GetPartialObject */
+    public static final int OPERATION_GET_PARTIAL_OBJECT = 0x101B;
+    /** Operation code for InitiateOpenCapture */
+    public static final int OPERATION_INITIATE_OPEN_CAPTURE = 0x101C;
+    /** Operation code for GetObjectPropsSupported */
+    public static final int OPERATION_GET_OBJECT_PROPS_SUPPORTED = 0x9801;
+    /** Operation code for GetObjectPropDesc */
+    public static final int OPERATION_GET_OBJECT_PROP_DESC = 0x9802;
+    /** Operation code for GetObjectPropValue */
+    public static final int OPERATION_GET_OBJECT_PROP_VALUE = 0x9803;
+    /** Operation code for SetObjectPropValue */
+    public static final int OPERATION_SET_OBJECT_PROP_VALUE = 0x9804;
+    /** Operation code for GetObjectReferences */
+    public static final int OPERATION_GET_OBJECT_REFERENCES = 0x9810;
+    /** Operation code for SetObjectReferences */
+    public static final int OPERATION_SET_OBJECT_REFERENCES = 0x9811;
+    /** Operation code for Skip */
+    public static final int OPERATION_SKIP = 0x9820;
 }
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 3541fba..bc96e2e 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -17,9 +17,9 @@
 package android.mtp;
 
 import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.content.ContentProviderClient;
 import android.content.ContentValues;
-import android.content.IContentProvider;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
@@ -37,23 +37,30 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import dalvik.system.CloseGuard;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * {@hide}
  */
-public class MtpDatabase {
-
+public class MtpDatabase implements AutoCloseable {
     private static final String TAG = "MtpDatabase";
 
     private final Context mContext;
     private final String mPackageName;
-    private final IContentProvider mMediaProvider;
+    private final ContentProviderClient mMediaProvider;
     private final String mVolumeName;
     private final Uri mObjectsUri;
+    private final MediaScanner mMediaScanner;
+
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
     // path to primary storage
     private final String mMediaStoragePath;
     // if not null, restrict all queries to these subdirectories
@@ -120,7 +127,6 @@
     private static final String STORAGE_FORMAT_PARENT_WHERE = STORAGE_FORMAT_WHERE + " AND "
                                             + Files.FileColumns.PARENT + "=?";
 
-    private final MediaScanner mMediaScanner;
     private MtpServer mServer;
 
     // read from native code
@@ -156,11 +162,12 @@
 
         mContext = context;
         mPackageName = context.getPackageName();
-        mMediaProvider = context.getContentResolver().acquireProvider("media");
+        mMediaProvider = context.getContentResolver()
+                .acquireContentProviderClient(MediaStore.AUTHORITY);
         mVolumeName = volumeName;
         mMediaStoragePath = storagePath;
         mObjectsUri = Files.getMtpObjectsUri(volumeName);
-        mMediaScanner = new MediaScanner(context);
+        mMediaScanner = new MediaScanner(context, mVolumeName);
 
         mSubDirectories = subDirectories;
         if (subDirectories != null) {
@@ -187,20 +194,9 @@
             }
         }
 
-        // Set locale to MediaScanner.
-        Locale locale = context.getResources().getConfiguration().locale;
-        if (locale != null) {
-            String language = locale.getLanguage();
-            String country = locale.getCountry();
-            if (language != null) {
-                if (country != null) {
-                    mMediaScanner.setLocale(language + "_" + country);
-                } else {
-                    mMediaScanner.setLocale(language);
-                }
-            }
-        }
         initDeviceProperties(context);
+
+        mCloseGuard.open("close");
     }
 
     public void setServer(MtpServer server) {
@@ -221,9 +217,20 @@
     }
 
     @Override
+    public void close() {
+        mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
+            mMediaScanner.close();
+            mMediaProvider.close();
+            native_finalize();
+        }
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         try {
-            native_finalize();
+            mCloseGuard.warnIfOpen();
+            close();
         } finally {
             super.finalize();
         }
@@ -334,7 +341,7 @@
         if (path != null) {
             Cursor c = null;
             try {
-                c = mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, PATH_WHERE,
+                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
                         new String[] { path }, null, null);
                 if (c != null && c.getCount() > 0) {
                     Log.w(TAG, "file already exists in beginSendObject: " + path);
@@ -359,7 +366,7 @@
         values.put(Files.FileColumns.DATE_MODIFIED, modified);
 
         try {
-            Uri uri = mMediaProvider.insert(mPackageName, mObjectsUri, values);
+            Uri uri = mMediaProvider.insert(mObjectsUri, values);
             if (uri != null) {
                 return Integer.parseInt(uri.getPathSegments().get(2));
             } else {
@@ -394,13 +401,13 @@
                 values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
                 values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
                 try {
-                    Uri uri = mMediaProvider.insert(mPackageName,
+                    Uri uri = mMediaProvider.insert(
                             Audio.Playlists.EXTERNAL_CONTENT_URI, values);
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException in endSendObject", e);
                 }
             } else {
-                mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
+                mMediaScanner.scanMtpFile(path, handle, format);
             }
         } else {
             deleteFile(handle);
@@ -503,7 +510,7 @@
             }
         }
 
-        return mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, where,
+        return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where,
                 whereArgs, null, null);
     }
 
@@ -721,7 +728,7 @@
              propertyGroup = mPropertyGroupsByFormat.get(format);
              if (propertyGroup == null) {
                 int[] propertyList = getSupportedObjectProperties(format);
-                propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mPackageName,
+                propertyGroup = new MtpPropertyGroup(this, mMediaProvider,
                         mVolumeName, propertyList);
                 mPropertyGroupsByFormat.put(new Integer(format), propertyGroup);
             }
@@ -729,7 +736,7 @@
               propertyGroup = mPropertyGroupsByProperty.get(property);
              if (propertyGroup == null) {
                 int[] propertyList = new int[] { (int)property };
-                propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mPackageName,
+                propertyGroup = new MtpPropertyGroup(this, mMediaProvider,
                         mVolumeName, propertyList);
                 mPropertyGroupsByProperty.put(new Integer((int)property), propertyGroup);
             }
@@ -745,7 +752,7 @@
         String path = null;
         String[] whereArgs = new String[] {  Integer.toString(handle) };
         try {
-            c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_PROJECTION, ID_WHERE,
+            c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE,
                     whereArgs, null, null);
             if (c != null && c.moveToNext()) {
                 path = c.getString(1);
@@ -788,7 +795,7 @@
         try {
             // note - we are relying on a special case in MediaProvider.update() to update
             // the paths for all children in the case where this is a directory.
-            updated = mMediaProvider.update(mPackageName, mObjectsUri, values, ID_WHERE, whereArgs);
+            updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in mMediaProvider.update", e);
         }
@@ -805,7 +812,7 @@
             if (oldFile.getName().startsWith(".") && !newPath.startsWith(".")) {
                 // directory was unhidden
                 try {
-                    mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, newPath, null);
+                    mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath, null);
                 } catch (RemoteException e) {
                     Log.e(TAG, "failed to unhide/rescan for " + newPath);
                 }
@@ -815,7 +822,7 @@
             if (oldFile.getName().toLowerCase(Locale.US).equals(".nomedia")
                     && !newPath.toLowerCase(Locale.US).equals(".nomedia")) {
                 try {
-                    mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
+                    mMediaProvider.call(MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
                 } catch (RemoteException e) {
                     Log.e(TAG, "failed to unhide/rescan for " + newPath);
                 }
@@ -886,7 +893,7 @@
                         char[] outName, long[] outCreatedModified) {
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mPackageName, mObjectsUri, OBJECT_INFO_PROJECTION,
+            c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
             if (c != null && c.moveToNext()) {
                 outStorageFormatParent[0] = c.getInt(1);
@@ -933,7 +940,7 @@
         }
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION,
+            c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
             if (c != null && c.moveToNext()) {
                 String path = c.getString(1);
@@ -960,7 +967,7 @@
     private int getObjectFormat(int handle) {
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mPackageName, mObjectsUri, FORMAT_PROJECTION,
+            c = mMediaProvider.query(mObjectsUri, FORMAT_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
             if (c != null && c.moveToNext()) {
                 return c.getInt(1);
@@ -984,7 +991,7 @@
 
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mPackageName, mObjectsUri, PATH_FORMAT_PROJECTION,
+            c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
                             ID_WHERE, new String[] {  Integer.toString(handle) }, null, null);
             if (c != null && c.moveToNext()) {
                 // don't convert to media path here, since we will be matching
@@ -1007,7 +1014,7 @@
             if (format == MtpConstants.FORMAT_ASSOCIATION) {
                 // recursive case - delete all children first
                 Uri uri = Files.getMtpObjectsUri(mVolumeName);
-                int count = mMediaProvider.delete(mPackageName, uri,
+                int count = mMediaProvider.delete(uri,
                     // the 'like' makes it use the index, the 'lower()' makes it correct
                     // when the path contains sqlite wildcard characters
                     "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
@@ -1015,12 +1022,12 @@
             }
 
             Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
-            if (mMediaProvider.delete(mPackageName, uri, null, null) > 0) {
+            if (mMediaProvider.delete(uri, null, null) > 0) {
                 if (format != MtpConstants.FORMAT_ASSOCIATION
                         && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
                     try {
                         String parentPath = path.substring(0, path.lastIndexOf("/"));
-                        mMediaProvider.call(mPackageName, MediaStore.UNHIDE_CALL, parentPath, null);
+                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null);
                     } catch (RemoteException e) {
                         Log.e(TAG, "failed to unhide/rescan for " + path);
                     }
@@ -1043,7 +1050,7 @@
         Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
         Cursor c = null;
         try {
-            c = mMediaProvider.query(mPackageName, uri, ID_PROJECTION, null, null, null, null);
+            c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null, null);
             if (c == null) {
                 return null;
             }
@@ -1077,7 +1084,7 @@
             valuesList[i] = values;
         }
         try {
-            if (mMediaProvider.bulkInsert(mPackageName, uri, valuesList) > 0) {
+            if (mMediaProvider.bulkInsert(uri, valuesList) > 0) {
                 return MtpConstants.RESPONSE_OK;
             }
         } catch (RemoteException e) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 95cb520..4379a99 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -19,9 +19,12 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
 
+import com.android.internal.util.Preconditions;
+
+import java.io.IOException;
+
 /**
  * This class represents an MTP or PTP device connected on the USB host bus. An application can
  * instantiate an object of this type, by referencing an attached {@link
@@ -158,6 +161,24 @@
     }
 
     /**
+     * Obtains object bytes in the specified range and writes it to an array.
+     * This call may block for an arbitrary amount of time depending on the size
+     * of the data and speed of the devices.
+     *
+     * @param objectHandle handle of the object to read
+     * @param offset Start index of reading range. It must be a non-negative value at most
+     *     0xffffffff.
+     * @param size Size of reading range. It must be a non-negative value at most 0xffffffff. If
+     *     0xffffffff is specified, the method obtains the full bytes of object.
+     * @param buffer Array to write data.
+     * @return Size of bytes that are actually read.
+     */
+    public long getPartialObject(int objectHandle, long offset, long size, byte[] buffer)
+            throws IOException {
+        return native_get_partial_object(objectHandle, offset, size, buffer);
+    }
+
+    /**
      * Returns the thumbnail data for an object as a byte array.
      * The size and format of the thumbnail data can be determined via
      * {@link MtpObjectInfo#getThumbCompressedSize} and
@@ -323,6 +344,8 @@
     private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
     private native MtpObjectInfo native_get_object_info(int objectHandle);
     private native byte[] native_get_object(int objectHandle, int objectSize);
+    private native long native_get_partial_object(
+            int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
     private native byte[] native_get_thumbnail(int objectHandle);
     private native boolean native_delete_object(int objectHandle);
     private native long native_get_parent(int objectHandle);
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
index ef9436d..1ceca84 100644
--- a/media/java/android/mtp/MtpDeviceInfo.java
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -16,6 +16,8 @@
 
 package android.mtp;
 
+import android.annotation.Nullable;
+
 /**
  * This class encapsulates information about an MTP device.
  * This corresponds to the DeviceInfo Dataset described in
@@ -27,6 +29,7 @@
     private String mModel;
     private String mVersion;
     private String mSerialNumber;
+    private int[] mOperationsSupported;
 
     // only instantiated via JNI
     private MtpDeviceInfo() {
@@ -67,4 +70,13 @@
     public final String getSerialNumber() {
         return mSerialNumber;
     }
-}
\ No newline at end of file
+
+    /**
+     * Returns operation code supported by the device.
+     *
+     * @return supported operation code
+     */
+    public final @Nullable int[] getOperationsSupported() {
+        return mOperationsSupported;
+    }
+}
diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java
index 4c8a742..dc89a56 100644
--- a/media/java/android/mtp/MtpEvent.java
+++ b/media/java/android/mtp/MtpEvent.java
@@ -18,29 +18,15 @@
 
 /**
  * This class encapsulates information about a MTP event.
- * Event constants are defined by the USB-IF MTP specification.
+ * This corresponds to the events described in appendix G of the MTP specification.
  */
 public class MtpEvent {
-    public static final int EVENT_UNDEFINED = 0x4000;
-    public static final int EVENT_CANCEL_TRANSACTION = 0x4001;
-    public static final int EVENT_OBJECT_ADDED = 0x4002;
-    public static final int EVENT_OBJECT_REMOVED = 0x4003;
-    public static final int EVENT_STORE_ADDED = 0x4004;
-    public static final int EVENT_STORE_REMOVED = 0x4005;
-    public static final int EVENT_DEVICE_PROP_CHANGED = 0x4006;
-    public static final int EVENT_OBJECT_INFO_CHANGED = 0x4007;
-    public static final int EVENT_DEVICE_INFO_CHANGED = 0x4008;
-    public static final int EVENT_REQUEST_OBJECT_TRANSFER = 0x4009;
-    public static final int EVENT_STORE_FULL = 0x400A;
-    public static final int EVENT_DEVICE_RESET = 0x400B;
-    public static final int EVENT_STORAGE_INFO_CHANGED = 0x400C;
-    public static final int EVENT_CAPTURE_COMPLETE = 0x400D;
-    public static final int EVENT_UNREPORTED_STATUS = 0x400E;
-    public static final int EVENT_OBJECT_PROP_CHANGED = 0xC801;
-    public static final int EVENT_OBJECT_PROP_DESC_CHANGED = 0xC802;
-    public static final int EVENT_OBJECT_REFERENCES_CHANGED = 0xC803;
+    private int mEventCode = MtpConstants.EVENT_UNDEFINED;
 
-    private int mEventCode = EVENT_UNDEFINED;
+    // Parameters for event. The interpretation of event parameters depends upon mEventCode.
+    private int mParameter1;
+    private int mParameter2;
+    private int mParameter3;
 
     /**
      * Returns event code of MTP event.
@@ -48,4 +34,136 @@
      * @return event code
      */
     public int getEventCode() { return mEventCode; }
+
+    /**
+     * Obtains the first event parameter.
+     */
+    public int getParameter1() { return mParameter1; }
+
+    /**
+     * Obtains the second event parameter.
+     */
+    public int getParameter2() { return mParameter2; }
+
+    /**
+     * Obtains the third event parameter.
+     */
+    public int getParameter3() { return mParameter3; }
+
+    /**
+     * Obtains objectHandle event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_ADDED
+     * @see MtpConstants#EVENT_OBJECT_REMOVED
+     * @see MtpConstants#EVENT_OBJECT_INFO_CHANGED
+     * @see MtpConstants#EVENT_REQUEST_OBJECT_TRANSFER
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_REFERENCES_CHANGED
+     */
+    public int getObjectHandle() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_ADDED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_REMOVED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_INFO_CHANGED:
+                return mParameter1;
+            case MtpConstants.EVENT_REQUEST_OBJECT_TRANSFER:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+                return mParameter1;
+            case MtpConstants.EVENT_OBJECT_REFERENCES_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("objectHandle", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains storageID event parameter.
+     *
+     * @see MtpConstants#EVENT_STORE_ADDED
+     * @see MtpConstants#EVENT_STORE_REMOVED
+     * @see MtpConstants#EVENT_STORE_FULL
+     * @see MtpConstants#EVENT_STORAGE_INFO_CHANGED
+     */
+    public int getStorageId() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_STORE_ADDED:
+                return mParameter1;
+            case MtpConstants.EVENT_STORE_REMOVED:
+                return mParameter1;
+            case MtpConstants.EVENT_STORE_FULL:
+                return mParameter1;
+            case MtpConstants.EVENT_STORAGE_INFO_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("storageID", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains devicePropCode event parameter.
+     *
+     * @see MtpConstants#EVENT_DEVICE_PROP_CHANGED
+     */
+    public int getDevicePropCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_DEVICE_PROP_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("devicePropCode", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains transactionID event parameter.
+     *
+     * @see MtpConstants#EVENT_CAPTURE_COMPLETE
+     */
+    public int getTransactionId() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_CAPTURE_COMPLETE:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("transactionID", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains objectPropCode event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     */
+    public int getObjectPropCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+                return mParameter2;
+            case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+                return mParameter1;
+            default:
+                throw new IllegalParameterAccess("objectPropCode", mEventCode);
+        }
+    }
+
+    /**
+     * Obtains objectFormatCode event parameter.
+     *
+     * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+     */
+    public int getObjectFormatCode() {
+        switch (mEventCode) {
+            case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+                return mParameter2;
+            default:
+                throw new IllegalParameterAccess("objectFormatCode", mEventCode);
+        }
+    }
+
+    private static class IllegalParameterAccess extends UnsupportedOperationException {
+        public IllegalParameterAccess(String propertyName, int eventCode) {
+            super("Cannot obtain " + propertyName + " for the event: " + eventCode + ".");
+        }
+    }
 }
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index c80adfa..dea3008 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -16,7 +16,7 @@
 
 package android.mtp;
 
-import android.content.IContentProvider;
+import android.content.ContentProviderClient;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -48,8 +48,7 @@
     }
 
     private final MtpDatabase mDatabase;
-    private final IContentProvider mProvider;
-    private final String mPackageName;
+    private final ContentProviderClient mProvider;
     private final String mVolumeName;
     private final Uri mUri;
 
@@ -65,13 +64,12 @@
     private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
     private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + FORMAT_WHERE;
     // constructs a property group for a list of properties
-    public MtpPropertyGroup(MtpDatabase database, IContentProvider provider, String packageName,
-            String volume, int[] properties) {
+    public MtpPropertyGroup(MtpDatabase database, ContentProviderClient provider, String volumeName,
+            int[] properties) {
         mDatabase = database;
         mProvider = provider;
-        mPackageName = packageName;
-        mVolumeName = volume;
-        mUri = Files.getMtpObjectsUri(volume);
+        mVolumeName = volumeName;
+        mUri = Files.getMtpObjectsUri(volumeName);
 
         int count = properties.length;
         ArrayList<String> columns = new ArrayList<String>(count);
@@ -201,7 +199,7 @@
         Cursor c = null;
         try {
             // for now we are only reading properties from the "objects" table
-            c = mProvider.query(mPackageName, mUri,
+            c = mProvider.query(mUri,
                             new String [] { Files.FileColumns._ID, column },
                             ID_WHERE, new String[] { Integer.toString(id) }, null, null);
             if (c != null && c.moveToNext()) {
@@ -221,7 +219,7 @@
     private String queryAudio(int id, String column) {
         Cursor c = null;
         try {
-            c = mProvider.query(mPackageName, Audio.Media.getContentUri(mVolumeName),
+            c = mProvider.query(Audio.Media.getContentUri(mVolumeName),
                             new String [] { Files.FileColumns._ID, column },
                             ID_WHERE, new String[] { Integer.toString(id) }, null, null);
             if (c != null && c.moveToNext()) {
@@ -242,7 +240,7 @@
         Cursor c = null;
         try {
             Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
-            c = mProvider.query(mPackageName, uri,
+            c = mProvider.query(uri,
                             new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
                             null, null, null, null);
             if (c != null && c.moveToNext()) {
@@ -264,7 +262,7 @@
         Cursor c = null;
         try {
             // for now we are only reading properties from the "objects" table
-            c = mProvider.query(mPackageName, mUri,
+            c = mProvider.query(mUri,
                             new String [] { Files.FileColumns._ID, column },
                             ID_WHERE, new String[] { Integer.toString(id) }, null, null);
             if (c != null && c.moveToNext()) {
@@ -335,7 +333,7 @@
         try {
             // don't query if not necessary
             if (depth > 0 || handle == 0xFFFFFFFF || mColumns.length > 1) {
-                c = mProvider.query(mPackageName, mUri, mColumns, where, whereArgs, null, null);
+                c = mProvider.query(mUri, mColumns, where, whereArgs, null, null);
                 if (c == null) {
                     return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
                 }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 49b579c..2004a3a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -85,6 +85,13 @@
     jmethodID setNativeObjectLocked;
 } gPersistentSurfaceClassInfo;
 
+static struct {
+    jint Unencrypted;
+    jint AesCtr;
+    jint AesCbc;
+} gCryptoModes;
+
+
 struct fields_t {
     jfieldID context;
     jmethodID postEventFromNativeID;
@@ -94,6 +101,9 @@
     jfieldID cryptoInfoKeyID;
     jfieldID cryptoInfoIVID;
     jfieldID cryptoInfoModeID;
+    jfieldID cryptoInfoPatternID;
+    jfieldID patternEncryptBlocksID;
+    jfieldID patternSkipBlocksID;
 };
 
 static fields_t gFields;
@@ -325,11 +335,12 @@
         const uint8_t key[16],
         const uint8_t iv[16],
         CryptoPlugin::Mode mode,
+        const CryptoPlugin::Pattern &pattern,
         int64_t presentationTimeUs,
         uint32_t flags,
         AString *errorDetailMsg) {
     return mCodec->queueSecureInputBuffer(
-            index, offset, subSamples, numSubSamples, key, iv, mode,
+            index, offset, subSamples, numSubSamples, key, iv, mode, pattern,
             presentationTimeUs, flags, errorDetailMsg);
 }
 
@@ -1275,7 +1286,26 @@
     jbyteArray ivObj =
         (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
 
-    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+    jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+    enum CryptoPlugin::Mode mode;
+    if (jmode == gCryptoModes.Unencrypted) {
+        mode = CryptoPlugin::kMode_Unencrypted;
+    } else if (jmode == gCryptoModes.AesCtr) {
+        mode = CryptoPlugin::kMode_AES_CTR;
+    } else if (jmode == gCryptoModes.AesCbc) {
+        mode = CryptoPlugin::kMode_AES_CBC;
+    }  else {
+        throwExceptionAsNecessary(env, INVALID_OPERATION);
+        return;
+    }
+
+    jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
+
+    CryptoPlugin::Pattern pattern;
+    if (patternObj != NULL) {
+        pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
+        pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
+    }
 
     status_t err = OK;
 
@@ -1360,7 +1390,8 @@
                 index, offset,
                 subSamples, numSubSamples,
                 (const uint8_t *)key, (const uint8_t *)iv,
-                (CryptoPlugin::Mode)mode,
+                mode,
+                pattern,
                 timestampUs,
                 flags,
                 &errorDetailMsg);
@@ -1658,6 +1689,22 @@
 
     CHECK(gFields.postEventFromNativeID != NULL);
 
+    jfieldID field;
+    field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I");
+    CHECK(field != NULL);
+    gCryptoModes.Unencrypted =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CTR", "I");
+    CHECK(field != NULL);
+    gCryptoModes.AesCtr =
+        env->GetStaticIntField(clazz.get(), field);
+
+    field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CBC", "I");
+    CHECK(field != NULL);
+    gCryptoModes.AesCbc =
+        env->GetStaticIntField(clazz.get(), field);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
     CHECK(clazz.get() != NULL);
 
@@ -1682,10 +1729,22 @@
     gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
     CHECK(gFields.cryptoInfoModeID != NULL);
 
+    gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern",
+        "Landroid/media/MediaCodec$CryptoInfo$Pattern;");
+    CHECK(gFields.cryptoInfoPatternID != NULL);
+
+    clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo$Pattern"));
+    CHECK(clazz.get() != NULL);
+
+    gFields.patternEncryptBlocksID = env->GetFieldID(clazz.get(), "mEncryptBlocks", "I");
+    CHECK(gFields.patternEncryptBlocksID != NULL);
+
+    gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I");
+    CHECK(gFields.patternSkipBlocksID != NULL);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
     CHECK(clazz.get() != NULL);
 
-    jfieldID field;
     field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
     CHECK(field != NULL);
     gCryptoErrorCodes.cryptoErrorNoKey =
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6650cf9..c0c47ef 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -81,6 +81,7 @@
             const uint8_t key[16],
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
+            const CryptoPlugin::Pattern &pattern,
             int64_t presentationTimeUs,
             uint32_t flags,
             AString *errorDetailMsg);
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 3f4d183..b1b3b62 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -27,8 +27,11 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "ScopedPrimitiveArray.h"
+
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "nativehelper/ScopedLocalRef.h"
 #include "private/android_filesystem_config.h"
 
 #include "MtpTypes.h"
@@ -41,6 +44,8 @@
 
 // ----------------------------------------------------------------------------
 
+namespace {
+
 static jfieldID field_context;
 
 jclass clazz_deviceInfo;
@@ -60,6 +65,7 @@
 static jfieldID field_deviceInfo_model;
 static jfieldID field_deviceInfo_version;
 static jfieldID field_deviceInfo_serialNumber;
+static jfieldID field_deviceInfo_operationsSupported;
 
 // MtpStorageInfo fields
 static jfieldID field_storageInfo_storageId;
@@ -92,6 +98,32 @@
 
 // MtpEvent fields
 static jfieldID field_event_eventCode;
+static jfieldID field_event_parameter1;
+static jfieldID field_event_parameter2;
+static jfieldID field_event_parameter3;
+
+class JavaArrayWriter {
+public:
+    JavaArrayWriter(JNIEnv* env, jbyteArray array) :
+        mEnv(env), mArray(array), mSize(mEnv->GetArrayLength(mArray)) {}
+    bool write(void* data, uint32_t offset, uint32_t length) {
+        if (static_cast<uint32_t>(mSize) < offset + length) {
+            return false;
+        }
+        mEnv->SetByteArrayRegion(mArray, offset, length, static_cast<jbyte*>(data));
+        return true;
+    }
+    static bool writeTo(void* data, uint32_t offset, uint32_t length, void* clientData) {
+        return static_cast<JavaArrayWriter*>(clientData)->write(data, offset, length);
+    }
+
+private:
+    JNIEnv* mEnv;
+    jbyteArray mArray;
+    jsize mSize;
+};
+
+}
 
 MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
 {
@@ -208,6 +240,17 @@
     if (deviceInfo->mSerial)
         env->SetObjectField(info, field_deviceInfo_serialNumber,
             env->NewStringUTF(deviceInfo->mSerial));
+    if (deviceInfo->mOperations) {
+        const size_t size = deviceInfo->mOperations->size();
+        const jintArray operations = env->NewIntArray(size);
+        {
+            ScopedIntArrayRW elements(env, operations);
+            for (size_t i = 0; i < size; ++i) {
+                elements[i] = deviceInfo->mOperations->itemAt(i);
+            }
+        }
+        env->SetObjectField(info, field_deviceInfo_operationsSupported, operations);
+    }
 
     delete deviceInfo;
     return info;
@@ -307,38 +350,72 @@
     return info;
 }
 
-struct get_object_callback_data {
-    JNIEnv *env;
-    jbyteArray array;
-};
-
-static bool get_object_callback(void* data, int offset, int length, void* clientData)
-{
-    get_object_callback_data* cbData = (get_object_callback_data *)clientData;
-    cbData->env->SetByteArrayRegion(cbData->array, offset, length, (jbyte *)data);
-    return true;
-}
-
 static jbyteArray
 android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize)
 {
     MtpDevice* device = get_device_from_object(env, thiz);
-    if (!device)
-        return NULL;
-
-    jbyteArray array = env->NewByteArray(objectSize);
-    if (!array) {
-        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
-        return NULL;
+    if (!device) {
+        return nullptr;
     }
 
-    get_object_callback_data data;
-    data.env = env;
-    data.array = array;
+    ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(objectSize));
+    if (!array.get()) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return nullptr;
+    }
 
-    if (device->readObject(objectID, get_object_callback, objectSize, &data))
-        return array;
-    return NULL;
+    JavaArrayWriter writer(env, array.get());
+
+    if (device->readObject(objectID, JavaArrayWriter::writeTo, objectSize, &writer)) {
+        return array.release();
+    }
+    return nullptr;
+}
+
+static jlong
+android_mtp_MtpDevice_get_partial_object(JNIEnv *env,
+                                         jobject thiz,
+                                         jint objectID,
+                                         jlong offset,
+                                         jlong size,
+                                         jbyteArray array)
+{
+    if (!array) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Array must not be null.");
+        return -1;
+    }
+
+    if (offset < 0 || 0xffffffffL < offset) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                "Offset argument must be a 32-bit unsigned integer.");
+        return -1;
+    }
+
+    if (size < 0 || 0xffffffffL < size) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                "Size argument must be a 32-bit unsigned integer.");
+        return -1;
+    }
+
+    MtpDevice* const device = get_device_from_object(env, thiz);
+    if (!device) {
+        jniThrowException(env, "java/io/IOException", "Failed to obtain MtpDevice.");
+        return -1;
+    }
+
+    JavaArrayWriter writer(env, array);
+    uint32_t written_size;
+    const bool success = device->readPartialObject(
+            objectID, offset, size, &written_size, JavaArrayWriter::writeTo, &writer);
+    if (!success) {
+        jniThrowException(env, "java/io/IOException", "Failed to read data.");
+        return -1;
+    }
+    return static_cast<jlong>(written_size);
 }
 
 static jbyteArray
@@ -512,13 +589,17 @@
         env->ThrowNew(clazz_io_exception, "");
         return NULL;
     }
-    const int eventCode = device->reapEventRequest(seq);
+    uint32_t parameters[3];
+    const int eventCode = device->reapEventRequest(seq, &parameters);
     if (eventCode <= 0) {
         env->ThrowNew(clazz_operation_canceled_exception, "");
         return NULL;
     }
     jobject result = env->NewObject(clazz_event, constructor_event);
     env->SetIntField(result, field_event_eventCode, eventCode);
+    env->SetIntField(result, field_event_parameter1, static_cast<jint>(parameters[0]));
+    env->SetIntField(result, field_event_parameter2, static_cast<jint>(parameters[1]));
+    env->SetIntField(result, field_event_parameter3, static_cast<jint>(parameters[2]));
     return result;
 }
 
@@ -547,6 +628,7 @@
     {"native_get_object_info",  "(I)Landroid/mtp/MtpObjectInfo;",
                                         (void *)android_mtp_MtpDevice_get_object_info},
     {"native_get_object",       "(II)[B",(void *)android_mtp_MtpDevice_get_object},
+    {"native_get_partial_object", "(IJJ[B)J", (void *)android_mtp_MtpDevice_get_partial_object},
     {"native_get_thumbnail",    "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
     {"native_delete_object",    "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
     {"native_get_parent",       "(I)J", (void *)android_mtp_MtpDevice_get_parent},
@@ -599,6 +681,11 @@
         ALOGE("Can't find MtpDeviceInfo.mSerialNumber");
         return -1;
     }
+    field_deviceInfo_operationsSupported = env->GetFieldID(clazz, "mOperationsSupported", "[I");
+    if (field_deviceInfo_operationsSupported == NULL) {
+        ALOGE("Can't find MtpDeviceInfo.mOperationsSupported");
+        return -1;
+    }
     clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz);
 
     clazz = env->FindClass("android/mtp/MtpStorageInfo");
@@ -765,6 +852,21 @@
         ALOGE("Can't find MtpObjectInfo.mEventCode");
         return -1;
     }
+    field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I");
+    if (field_event_parameter1 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter1");
+        return -1;
+    }
+    field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I");
+    if (field_event_parameter2 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter2");
+        return -1;
+    }
+    field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I");
+    if (field_event_parameter3 == NULL) {
+        ALOGE("Can't find MtpObjectInfo.mParameter3");
+        return -1;
+    }
     clazz_event = (jclass)env->NewGlobalRef(clazz);
 
     clazz = env->FindClass("android/mtp/MtpDevice");
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 70d651f..b63df6f 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -655,7 +655,7 @@
        goto error;
     }
 
-    if ((numChannels < 1) || (numChannels > 8)) {
+    if ((numChannels < 1) || (numChannels > FCC_8)) {
         ALOGE("Sample channel count (%d) out of range", numChannels);
         status = BAD_VALUE;
         goto error;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 05df014..06efa90 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -16,6 +16,7 @@
 
 package com.android.mediaframeworktest.unit;
 
+import android.content.ContentProviderClient;
 import android.content.ContentValues;
 import android.content.IContentProvider;
 import android.media.MediaInserter;
@@ -81,8 +82,9 @@
     protected void setUp() throws Exception {
         super.setUp();
         mMockProvider = EasyMock.createMock(IContentProvider.class);
-        mMediaInserter = new MediaInserter(mMockProvider,
-        mPackageName, TEST_BUFFER_SIZE);
+        final ContentProviderClient client = new ContentProviderClient(
+                getInstrumentation().getContext().getContentResolver(), mMockProvider, true);
+        mMediaInserter = new MediaInserter(client, TEST_BUFFER_SIZE);
         mPackageName = getInstrumentation().getContext().getPackageName();
         mFilesCounter = 0;
         mAudioCounter = 0;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 359a7a9..9c01f4f 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -161,7 +161,7 @@
  * </pre>
  *
  */
-public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
     private final static String TAG = "GLSurfaceView";
     private final static boolean LOG_ATTACH_DETACH = false;
     private final static boolean LOG_THREADS = false;
@@ -542,6 +542,16 @@
     }
 
     /**
+     * This method is part of the SurfaceHolder.Callback interface, and is
+     * not normally called or subclassed by clients of GLSurfaceView.
+     */
+    @Override
+    public void surfaceRedrawNeeded(SurfaceHolder holder) {
+        mGLThread.requestRenderAndWait();
+    }
+
+
+    /**
      * Inform the view that the activity is paused. The owner of this view must
      * call this method when the activity is paused. Calling this method will
      * pause the rendering thread.
@@ -1226,6 +1236,7 @@
             mHeight = 0;
             mRequestRender = true;
             mRenderMode = RENDERMODE_CONTINUOUSLY;
+            mWantRenderNotification = false;
             mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
         }
 
@@ -1271,6 +1282,8 @@
             mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
             mHaveEglContext = false;
             mHaveEglSurface = false;
+            mWantRenderNotification = false;
+
             try {
                 GL10 gl = null;
                 boolean createEglContext = false;
@@ -1278,7 +1291,6 @@
                 boolean createGlInterface = false;
                 boolean lostEglContext = false;
                 boolean sizeChanged = false;
-                boolean wantRenderNotification = false;
                 boolean doRenderNotification = false;
                 boolean askedToReleaseEglContext = false;
                 int w = 0;
@@ -1383,7 +1395,7 @@
                                 if (LOG_SURFACE) {
                                     Log.i("GLThread", "sending render notification tid=" + getId());
                                 }
-                                wantRenderNotification = false;
+                                mWantRenderNotification = false;
                                 doRenderNotification = false;
                                 mRenderComplete = true;
                                 sGLThreadManager.notifyAll();
@@ -1422,7 +1434,7 @@
                                         sizeChanged = true;
                                         w = mWidth;
                                         h = mHeight;
-                                        wantRenderNotification = true;
+                                        mWantRenderNotification = true;
                                         if (LOG_SURFACE) {
                                             Log.i("GLThread",
                                                     "noticing that we want render notification tid="
@@ -1562,7 +1574,7 @@
                             break;
                     }
 
-                    if (wantRenderNotification) {
+                    if (mWantRenderNotification) {
                         doRenderNotification = true;
                     }
                 }
@@ -1611,6 +1623,23 @@
             }
         }
 
+        public void requestRenderAndWait() {
+            synchronized(sGLThreadManager) {
+                mWantRenderNotification = true;
+                mRequestRender = true;
+                mRenderComplete = false;
+                sGLThreadManager.notifyAll();
+                while (!mExited && !mPaused && mRenderComplete == false) {
+                    try {
+                        sGLThreadManager.wait();
+                    } catch (InterruptedException ex) {
+                        Thread.currentThread().interrupt();
+                    }
+                }
+
+            }
+        }
+
         public void surfaceCreated() {
             synchronized(sGLThreadManager) {
                 if (LOG_THREADS) {
@@ -1766,6 +1795,7 @@
         private int mHeight;
         private int mRenderMode;
         private boolean mRequestRender;
+        private boolean mWantRenderNotification;
         private boolean mRenderComplete;
         private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
         private boolean mSizeChanged = true;
diff --git a/packages/BackupRestoreConfirmation/res/values-de/strings.xml b/packages/BackupRestoreConfirmation/res/values-de/strings.xml
index a2e24e7..e1f797f 100644
--- a/packages/BackupRestoreConfirmation/res/values-de/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-de/strings.xml
@@ -18,19 +18,19 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="backup_confirm_title" msgid="827563724209303345">"Vollständige Sicherung"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"Vollständige Wiederherstellung"</string>
-    <string name="backup_confirm_text" msgid="1878021282758896593">"Es wurde eine vollständige Sicherung sämtlicher Daten auf einen verbundenen Desktop-Computer angefordert. Möchten Sie dies zulassen?\n\nWenn Sie die Sicherung nicht selbst angefordert haben, sollten Sie dem Vorgang nicht zustimmen."</string>
+    <string name="backup_confirm_text" msgid="1878021282758896593">"Es wurde eine vollständige Sicherung sämtlicher Daten auf einen verbundenen Desktop-Computer angefordert. Möchtest du dies zulassen?\n\nWenn du die Sicherung nicht selbst angefordert hast, solltest du dem Vorgang nicht zustimmen."</string>
     <string name="allow_backup_button_label" msgid="4217228747769644068">"Meine Daten sichern"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"Nicht sichern"</string>
-    <string name="restore_confirm_text" msgid="7499866728030461776">"Es wurde eine vollständige Wiederherstellung aller Daten von einem verbundenen Desktop-Computer angefordert. Möchten Sie dies zulassen?\n\nWenn Sie die Wiederherstellung nicht selbst angefordert haben, sollten Sie dem Vorgang nicht zustimmen. Dadurch werden alle zurzeit auf dem Gerät befindlichen Daten ersetzt!"</string>
+    <string name="restore_confirm_text" msgid="7499866728030461776">"Es wurde eine vollständige Wiederherstellung aller Daten von einem verbundenen Desktop-Computer angefordert. Möchtest du dies zulassen?\n\nWenn du die Wiederherstellung nicht selbst angefordert hast, solltest du dem Vorgang nicht zustimmen. Dadurch werden alle derzeit auf dem Gerät befindlichen Daten ersetzt!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Meine Daten wiederherstellen"</string>
     <string name="deny_restore_button_label" msgid="1724367334453104378">"Nicht wiederherstellen"</string>
-    <string name="current_password_text" msgid="8268189555578298067">"Geben Sie Ihr aktuelles Sicherungspasswort unten ein:"</string>
-    <string name="device_encryption_restore_text" msgid="1570864916855208992">"Geben Sie Ihr Passwort zur Geräteverschlüsselung unten ein."</string>
-    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geben Sie Ihr Passwort zur Geräteverschlüsselung unten ein. Damit wird auch das Sicherungsarchiv verschlüsselt."</string>
-    <string name="backup_enc_password_text" msgid="4981585714795233099">"Geben Sie ein Passwort für die Verschlüsselung der vollständigen Sicherungsdaten ein. Wenn Sie dieses Feld leer lassen, wird Ihr aktuelles Sicherungspasswort verwendet:"</string>
-    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Wenn Sie die gesamten Sicherungsdaten verschlüsseln möchten, geben Sie unten ein Passwort ein:"</string>
+    <string name="current_password_text" msgid="8268189555578298067">"Gib dein aktuelles Sicherungspasswort unten ein:"</string>
+    <string name="device_encryption_restore_text" msgid="1570864916855208992">"Gib dein Passwort zur Geräteverschlüsselung unten ein."</string>
+    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Gib dein Passwort zur Geräteverschlüsselung unten ein. Damit wird auch das Sicherungsarchiv verschlüsselt."</string>
+    <string name="backup_enc_password_text" msgid="4981585714795233099">"Gib ein Passwort für die Verschlüsselung der vollständigen Sicherungsdaten ein. Wenn du dieses Feld leer lässt, wird dein aktuelles Sicherungspasswort verwendet:"</string>
+    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Wenn du die gesamten Sicherungsdaten verschlüsseln möchtest, gib unten ein Passwort ein:"</string>
     <string name="backup_enc_password_required" msgid="7889652203371654149">"Da Ihr Gerät verschlüsselt ist, muss auch die Sicherung verschlüsselt werden. Geben Sie unten ein Passwort ein:"</string>
-    <string name="restore_enc_password_text" msgid="6140898525580710823">"Geben Sie das Passwort unten ein, wenn die Daten für die Wiederherstellung verschlüsselt sind:"</string>
+    <string name="restore_enc_password_text" msgid="6140898525580710823">"Gib das Passwort unten ein, wenn die Daten für die Wiederherstellung verschlüsselt sind:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Sicherung wird gestartet..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Sicherung abgeschlossen"</string>
     <string name="toast_restore_started" msgid="7881679218971277385">"Wiederherstellung wird gestartet..."</string>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index abb464e..6fb8b51 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,7 +56,6 @@
 
 public class CaptivePortalLoginActivity extends Activity {
     private static final String TAG = "CaptivePortalLogin";
-    private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
     private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
@@ -72,16 +71,14 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
-        if (server == null) server = DEFAULT_SERVER;
         mCm = ConnectivityManager.from(this);
         String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
+        if (url == null) url = mCm.getCaptivePortalServerUrl();
         try {
-            mURL = url != null ? new URL(url) : new URL("http", server, "/generate_204");
+            mURL = new URL(url);
         } catch (MalformedURLException e) {
             // System misconfigured, bail out in a way that at least provides network access.
-            Log.e(TAG, "Invalid captive portal URL, server=" + server);
+            Log.e(TAG, "Invalid captive portal URL, url=" + url);
             done(Result.WANTED_AS_IS);
         }
         mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 5e634a4..6beef44 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -92,7 +92,7 @@
         </receiver>
 
         <service
-            android:name=".CopyService"
+            android:name=".services.FileOperationService"
             android:exported="false">
         </service>
     </application>
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
deleted file mode 100644
index 2b21c12..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
deleted file mode 100644
index ed3ee45..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
deleted file mode 100644
index 1a3ebc47..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate_alpha.png
deleted file mode 100644
index 3086a69..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_certificate_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes_alpha.png
deleted file mode 100644
index b86f0f7..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_codes_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed_alpha.png
deleted file mode 100644
index 9d0988d..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_compressed_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_contact_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_contact_am_alpha.png
deleted file mode 100644
index 6c31360..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_contact_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_event_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_event_am_alpha.png
deleted file mode 100644
index 1450ff0..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_event_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel_alpha.png
deleted file mode 100644
index 4ad54bb..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_excel_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_folder_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_folder_alpha.png
deleted file mode 100644
index 7f7c636..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font_alpha.png
deleted file mode 100644
index d867847..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_font_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_generic_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_generic_am_alpha.png
deleted file mode 100644
index 5459767..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_generic_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_alpha.png
deleted file mode 100644
index 0cbd992..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_image_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf_alpha.png
deleted file mode 100644
index db46702..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_pdf_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint_alpha.png
deleted file mode 100644
index b9f7af5..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_powerpoint_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation_alpha.png
deleted file mode 100644
index 1218c2f..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_presentation_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_spreadsheet_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_spreadsheet_am_alpha.png
deleted file mode 100644
index 46d755f..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_spreadsheet_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_text_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_text_am_alpha.png
deleted file mode 100644
index 933570b..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_text_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_am_alpha.png
deleted file mode 100644
index 30cea25..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_video_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word_alpha.png
deleted file mode 100644
index 67b60de..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_word_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 65e42aa..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
deleted file mode 100644
index ac27eea..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
deleted file mode 100644
index a4add51..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
deleted file mode 100644
index a9a7f20..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate_alpha.png
deleted file mode 100644
index 26beb79..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_certificate_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes_alpha.png
deleted file mode 100644
index ed9cab7..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_codes_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed_alpha.png
deleted file mode 100644
index 451d287..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_compressed_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_contact_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_contact_am_alpha.png
deleted file mode 100644
index 7bc02c9..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_contact_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_event_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_event_am_alpha.png
deleted file mode 100644
index de39cd4..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_event_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel_alpha.png
deleted file mode 100644
index c3eb845..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_excel_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_folder_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_folder_alpha.png
deleted file mode 100644
index 97f6e507..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font_alpha.png
deleted file mode 100644
index b2f043f..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_font_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_generic_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_generic_am_alpha.png
deleted file mode 100644
index 483d7fb..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_generic_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_alpha.png
deleted file mode 100644
index 2941b8a..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_image_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf_alpha.png
deleted file mode 100644
index d3ac072..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_pdf_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint_alpha.png
deleted file mode 100644
index a43f902..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_powerpoint_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation_alpha.png
deleted file mode 100644
index c572cd1..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_presentation_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_spreadsheet_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_spreadsheet_am_alpha.png
deleted file mode 100644
index 7ae671b..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_spreadsheet_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_text_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_text_am_alpha.png
deleted file mode 100644
index a5ebd51..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_text_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_am_alpha.png
deleted file mode 100644
index 89ac434..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_video_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word_alpha.png
deleted file mode 100644
index 855c44b..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_word_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 1de8292..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
deleted file mode 100644
index 4203d35..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
deleted file mode 100644
index 41558f2..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
deleted file mode 100644
index e190c4d..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate_alpha.png
deleted file mode 100644
index fabc8c3..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_certificate_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes_alpha.png
deleted file mode 100644
index d8ba6e0..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_codes_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed_alpha.png
deleted file mode 100644
index 1b5111a..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_compressed_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_contact_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_contact_am_alpha.png
deleted file mode 100644
index e7b7460..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_contact_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_event_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_event_am_alpha.png
deleted file mode 100644
index 3183bfc..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_event_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel_alpha.png
deleted file mode 100644
index 9a4e844..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_excel_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_folder_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_folder_alpha.png
deleted file mode 100644
index 96d5721..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font_alpha.png
deleted file mode 100644
index 23a1302..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_font_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_generic_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_generic_am_alpha.png
deleted file mode 100644
index b0811f1..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_generic_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_alpha.png
deleted file mode 100644
index 5f1f537..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_image_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf_alpha.png
deleted file mode 100644
index 8083584..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_pdf_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint_alpha.png
deleted file mode 100644
index 898e26a..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_powerpoint_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation_alpha.png
deleted file mode 100644
index 2e9c667..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_presentation_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_spreadsheet_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_spreadsheet_am_alpha.png
deleted file mode 100644
index 56951c3..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_spreadsheet_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_text_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_text_am_alpha.png
deleted file mode 100644
index 08ed9f0..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_text_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_am_alpha.png
deleted file mode 100644
index cd64e56..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_video_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word_alpha.png
deleted file mode 100644
index 0bc40de..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_word_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 00b8a8b7..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
deleted file mode 100644
index 60f59f5..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
deleted file mode 100644
index 6006b12..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
deleted file mode 100644
index 7926188..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate_alpha.png
deleted file mode 100644
index e65d74c..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_certificate_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes_alpha.png
deleted file mode 100644
index 7d32801..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_codes_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed_alpha.png
deleted file mode 100644
index d528708..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_compressed_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_contact_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_contact_am_alpha.png
deleted file mode 100644
index 274c524..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_contact_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_event_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_event_am_alpha.png
deleted file mode 100644
index 6053b0a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_event_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel_alpha.png
deleted file mode 100644
index 78ee13b..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_excel_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_folder_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_folder_alpha.png
deleted file mode 100644
index dcf9cc2..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font_alpha.png
deleted file mode 100644
index 74fbbbc..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_font_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_generic_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_generic_am_alpha.png
deleted file mode 100644
index 11a219a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_generic_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_alpha.png
deleted file mode 100644
index 9a461c9..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_image_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf_alpha.png
deleted file mode 100644
index 9a67956..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_pdf_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint_alpha.png
deleted file mode 100644
index 853aa8c..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_powerpoint_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation_alpha.png
deleted file mode 100644
index 84ae9ae..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_presentation_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_spreadsheet_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_spreadsheet_am_alpha.png
deleted file mode 100644
index 111439e..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_spreadsheet_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_text_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_text_am_alpha.png
deleted file mode 100644
index b876f86..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_text_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_am_alpha.png
deleted file mode 100644
index 7cf656a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_video_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word_alpha.png
deleted file mode 100644
index acc3d7b..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_word_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index 5236774..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
deleted file mode 100644
index d2dd494..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
deleted file mode 100644
index 4f935bf..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
deleted file mode 100644
index 7499cbc..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_certificate_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_certificate_alpha.png
deleted file mode 100644
index ca12928..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_certificate_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_codes_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_codes_alpha.png
deleted file mode 100644
index c4afa37..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_codes_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_compressed_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_compressed_alpha.png
deleted file mode 100644
index 0b0aa04..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_compressed_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_contact_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_contact_am_alpha.png
deleted file mode 100644
index ebd0535..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_contact_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_event_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_event_am_alpha.png
deleted file mode 100644
index 29cdbb7..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_event_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_excel_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_excel_alpha.png
deleted file mode 100644
index ca349b6..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_excel_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_folder_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_folder_alpha.png
deleted file mode 100644
index 02249d2..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_font_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_font_alpha.png
deleted file mode 100644
index 469b911..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_font_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_generic_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_generic_am_alpha.png
deleted file mode 100644
index ef479c3..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_generic_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_image_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_image_alpha.png
deleted file mode 100644
index 3168d6f..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_image_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_pdf_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_pdf_alpha.png
deleted file mode 100644
index 9bb4d66..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_pdf_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_powerpoint_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_powerpoint_alpha.png
deleted file mode 100644
index 88ba9ad..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_powerpoint_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_presentation_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_presentation_alpha.png
deleted file mode 100644
index 5fe18da..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_presentation_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_spreadsheet_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_spreadsheet_am_alpha.png
deleted file mode 100644
index 1d05f6f..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_spreadsheet_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_text_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_text_am_alpha.png
deleted file mode 100644
index c2308fe..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_text_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_video_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_video_am_alpha.png
deleted file mode 100644
index 9a173be..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_video_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_word_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_word_alpha.png
deleted file mode 100644
index a564f5a..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_word_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
deleted file mode 100644
index c7daa2a..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/ic_check_circle.xml b/packages/DocumentsUI/res/drawable/ic_check_circle.xml
new file mode 100644
index 0000000..d49ba6a
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_check_circle.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF009688"
+        android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10,-4.48 10,-10S17.52 2 12 2zm-2 15l-5,-5 1.41,-1.41L10 14.17l7.59,-7.59L19 8l-9 9z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_album.xml b/packages/DocumentsUI/res/drawable/ic_doc_album.xml
index e7965e6..1ce3f02 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_album.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_album.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_album_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10,-4.48 10,-10S17.52 2 12 2zm0 14.5c-2.49 0,-4.5,-2.01,-4.5,-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5,-2.01 4.5,-4.5 4.5zm0,-5.5c-.55 0,-1 .45,-1 1s.45 1 1 1 1,-.45 1,-1,-.45,-1,-1,-1z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_apk.xml b/packages/DocumentsUI/res/drawable/ic_doc_apk.xml
index 4f8f06e..197445e 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_apk.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_apk.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_apk_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M6 18c0 .55.45 1 1 1h1v3.5c0 .83.67 1.5 1.5 1.5s1.5,-.67 1.5,-1.5V19h2v3.5c0 .83.67 1.5 1.5 1.5s1.5,-.67 1.5,-1.5V19h1c.55 0 1,-.45 1,-1V8H6v10zM3.5 8C2.67 8 2 8.67 2 9.5v7c0 .83.67 1.5 1.5 1.5S5 17.33 5 16.5v-7C5 8.67 4.33 8 3.5 8zm17 0c-.83 0,-1.5.67,-1.5 1.5v7c0 .83.67 1.5 1.5 1.5s1.5,-.67 1.5,-1.5v-7c0,-.83,-.67,-1.5,-1.5,-1.5zm-4.97,-5.84l1.3,-1.3c.2,-.2.2,-.51 0,-.71,-.2,-.2,-.51,-.2,-.71 0l-1.48 1.48C13.85 1.23 12.95 1 12 1c-.96 0,-1.86.23,-2.66.63L7.85.15c-.2,-.2,-.51,-.2,-.71 0,-.2.2,-.2.51 0 .71l1.31 1.31C6.97 3.26 6 5.01 6 7h12c0,-1.99,-.97,-3.75,-2.47,-4.84zM10 5H9V4h1v1zm5 0h-1V4h1v1z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_audio.xml b/packages/DocumentsUI/res/drawable/ic_doc_audio.xml
index cf18b4f..454eea3 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_audio.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_audio.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_audio_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFDB4437"
+        android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zM7.2 18c-.66 0,-1.2,-.54,-1.2,-1.2V12c0,-3.31 2.69,-6 6,-6s6 2.69 6 6v4.8c0 .66,-.54 1.2,-1.2 1.2H14v-4h2v-2c0,-2.21,-1.79,-4,-4,-4s-4 1.79,-4 4v2h2v4H7.2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml b/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml
index c28ed4f..b99baf6 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_certificate_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M17.81 4.47c-.08 0,-.16,-.02,-.23,-.06C15.66 3.42 14 3 12.01 3c-1.98 0,-3.86.47,-5.57 1.41,-.24.13,-.54.04,-.68,-.2,-.13,-.24,-.04,-.55.2,-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67,-.09.18,-.26.28,-.44.28zM3.5 9.72c-.1 0,-.2,-.03,-.29,-.09,-.23,-.16,-.28,-.47,-.12,-.7.99,-1.4 2.25,-2.5 3.75,-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54,-.12.7,-.23.16,-.54.11,-.7,-.12,-.9,-1.26,-2.04,-2.25,-3.39,-2.94,-2.87,-1.47,-6.54,-1.47,-9.4.01,-1.36.7,-2.5 1.7,-3.4 2.96,-.08.14,-.23.21,-.39.21zm6.25 12.07c-.13 0,-.26,-.05,-.35,-.15,-.87,-.87,-1.34,-1.43,-2.01,-2.64,-.69,-1.23,-1.05,-2.73,-1.05,-4.34 0,-2.97 2.54,-5.39 5.66,-5.39s5.66 2.42 5.66 5.39c0 .28,-.22.5,-.5.5s-.5,-.22,-.5,-.5c0,-2.42,-2.09,-4.39,-4.66,-4.39,-2.57 0,-4.66 1.97,-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71,-.11.1,-.24.15,-.37.15zm7.17,-1.85c-1.19 0,-2.24,-.3,-3.1,-.89,-1.49,-1.01,-2.38,-2.65,-2.38,-4.39 0,-.28.22,-.5.5,-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64,-.03 1.04,-.1.27,-.05.53.13.58.41.05.27,-.13.53,-.41.58,-.57.11,-1.07.12,-1.21.12zM14.91 22c-.04 0,-.09,-.01,-.13,-.02,-1.59,-.44,-2.63,-1.03,-3.72,-2.1,-1.4,-1.39,-2.17,-3.24,-2.17,-5.22 0,-1.62 1.38,-2.94 3.08,-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08,-.87 2.08,-1.94c0,-3.77,-3.25,-6.83,-7.25,-6.83,-2.84 0,-5.44 1.58,-6.61 4.03,-.39.81,-.59 1.76,-.59 2.8 0 .78.07 2.01.67 3.61.1.26,-.03.55,-.29.64,-.26.1,-.55,-.04,-.64,-.29,-.49,-1.31,-.73,-2.61,-.73,-3.96 0,-1.2.23,-2.29.68,-3.24 1.33,-2.79 4.28,-4.6 7.51,-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62,-1.38 2.94,-3.08 2.94s-3.08,-1.32,-3.08,-2.94c0,-1.07,-.93,-1.94,-2.08,-1.94s-2.08.87,-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61,-.05.23,-.26.38,-.47.38z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_codes.xml b/packages/DocumentsUI/res/drawable/ic_doc_codes.xml
index 0de721a9..ea1c464 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_codes.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_codes.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_codes_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M9.4 16.6L4.8 12l4.6,-4.6L8 6l-6 6 6 6 1.4,-1.4zm5.2 0l4.6,-4.6,-4.6,-4.6L16 6l6 6,-6 6,-1.4,-1.4z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml b/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml
index a49cfa4..e0eb669 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_compressed_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm-5 6h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2v2zm0 8h-2v-2h-2v-2h2v2h2v2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_contact.xml b/packages/DocumentsUI/res/drawable/ic_doc_contact.xml
index bf550cb..a77cb6b 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_contact.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_contact.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_contact_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M19 3H5c-1.11 0,-2 .89,-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.11,-.9,-2,-2,-2zm-7 3c1.65 0 3 1.35 3 3 0 1.66,-1.35 3,-3 3s-3,-1.34,-3,-3c0,-1.65 1.35,-3 3,-3zm6 12H6v-1c0,-2 4,-3.1 6,-3.1s6 1.1 6 3.1v1z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_document.xml b/packages/DocumentsUI/res/drawable/ic_doc_document.xml
new file mode 100644
index 0000000..29251ad
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_doc_document.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF4883F3"
+        android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm-1.99 6H7V7h10.01v2zm0 4H7v-2h10.01v2zm-3 4H7v-2h7.01v2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_event.xml b/packages/DocumentsUI/res/drawable/ic_doc_event.xml
index 25cf0f3..113f079 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_event.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_event.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_event_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0,-1.99.9,-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2h-1V1h-2zm3 18H5V8h14v11z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_excel.xml b/packages/DocumentsUI/res/drawable/ic_doc_excel.xml
index 3354725..3ed27e1 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_excel.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_excel.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_excel_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF16A765"
+        android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm-2.8 14h-2L12 13.2 9.8 17h-2l3.2,-5,-3.2,-5h2l2.2 3.8L14.2 7h2L13 12l3.2 5z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_folder.xml b/packages/DocumentsUI/res/drawable/ic_doc_folder.xml
index 73de60e..dcbce01 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_folder.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_folder.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_folder_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M10 4H4c-1.1 0,-1.99.9,-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2,-.9 2,-2V8c0,-1.1,-.9,-2,-2,-2h-8l-2,-2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_font.xml b/packages/DocumentsUI/res/drawable/ic_doc_font.xml
index c74cb9c..4c13d71 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_font.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_font.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_font_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M9.93 13.5h4.14L12 7.98zM20 2H4c-1.1 0,-2 .9,-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2,-.9 2,-2V4c0,-1.1,-.9,-2,-2,-2zm-4.05 16.5l-1.14,-3H9.17l-1.12 3H5.96l5.11,-13h1.86l5.11 13h-2.09z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_generic.xml b/packages/DocumentsUI/res/drawable/ic_doc_generic.xml
index a4ee29d..006dfba 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_generic.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_generic.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_generic_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M6 2c-1.1 0,-1.99.9,-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2,-.9 2,-2V8l-6,-6H6zm7 7V3.5L18.5 9H13z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_image.xml b/packages/DocumentsUI/res/drawable/ic_doc_image.xml
index 9d4c359..23953f7 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_image.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_image.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_image_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFDB4437"
+        android:pathData="M21 19V5c0,-1.1,-.9,-2,-2,-2H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5,-4.5z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml b/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml
index 5c37498..b2d0193 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_pdf_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFDB4437"
+        android:pathData="M7 11.5h1v-1H7v1zM19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm-9.5 8.5c0 .83,-.67 1.5,-1.5 1.5H7v2H5.5V9H8c.83 0 1.5.67 1.5 1.5v1zm10,-1H17v1h1.5V13H17v2h-1.5V9h4v1.5zm-5 3c0 .83,-.67 1.5,-1.5 1.5h-2.5V9H13c.83 0 1.5.67 1.5 1.5v3zm-2.5 0h1v-3h-1v3z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml b/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml
index f0a6c39..aa5bfc8 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_powerpoint_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFF7537"
+        android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zM9.8 13.4V17H8V7h4.3c1.53 0 2.15.3 2.8.89.65.59.9 1.37.9 2.34 0 1.02,-.26 1.8,-.9 2.35s-1.3.82,-2.8.82H9.8zm0,-1.4V8.4h2.3c.66 0 1.17.25 1.5.6.33.35.5.72.5 1.25 0 .55,-.18.95,-.5 1.25,-.32.31,-.7.5,-1.38.5H9.8z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml b/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml
index a14f866..7937bc1 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_presentation_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFF4B400"
+        android:pathData="M19 3H5c-1.1 0,-1.99.9,-1.99 2v14c0 1.1.89 2 1.99 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm0 13H5V8h14v8z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml b/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml
index 40f2515..1663c0a 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_spreadsheet_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF16A765"
+        android:pathData="M19 3H5c-1.1 0,-1.99.9,-1.99 2L3 8v11c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm0 8h-8v8H9v-8H5V9h4V5h2v4h8v2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_text.xml b/packages/DocumentsUI/res/drawable/ic_doc_text.xml
index ffa9e70..7fc04e8 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_text.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_text.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_text_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF737373"
+        android:pathData="M14 2H6c-1.1 0,-1.99.9,-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2,-.9 2,-2V8l-6,-6zm2 16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5 9H13z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_video.xml b/packages/DocumentsUI/res/drawable/ic_doc_video.xml
index 2d048c2..ad4dae8 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_video.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_video.xml
@@ -1,5 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_video_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFDB4437"
+        android:pathData="M18 4l2 4h-3l-2,-4h-2l2 4h-3l-2,-4H8l2 4H7L5 4H4c-1.1 0,-1.99.9,-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2,-.9 2,-2V4h-4z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_word.xml b/packages/DocumentsUI/res/drawable/ic_doc_word.xml
index afcc533..7a3a0ec 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_word.xml
+++ b/packages/DocumentsUI/res/drawable/ic_doc_word.xml
@@ -1,4 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_doc_word_alpha"
-    android:tint="?android:attr/colorControlNormal" />
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF4883F3"
+        android:pathData="M19 3H5c-1.1 0,-2 .9,-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2,-.9 2,-2V5c0,-1.1,-.9,-2,-2,-2zm-3.5 14H14l-2,-7.5,-2 7.5H8.5L6.1 7h1.7l1.54 7.51L11.3 7h1.4l1.97 7.51L16.2 7h1.7l-2.4 10z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_home.xml
index 0a258ac..696ee05 100644
--- a/packages/DocumentsUI/res/drawable/ic_root_home.xml
+++ b/packages/DocumentsUI/res/drawable/ic_root_home.xml
@@ -1,15 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+<!--
+Copyright (C) 2015 The Android Open Source Project
 
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <path
-        android:fillColor="#000000"
-        android:pathData="M20 6h-8l-2-2H4c-1.1 0-1.99 .9 -1.99 2L2 18c0 1.1 .9 2 2 2h16c1.1 0 2-.9
-2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4
-8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z" />
-    <path
-        android:pathData="M0 0h24v24H0z" />
+        android:fillColor="#FF000000"
+        android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
 </vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_root_sdcard.xml b/packages/DocumentsUI/res/drawable/ic_root_sdcard.xml
deleted file mode 100644
index 27cfa81d..0000000
--- a/packages/DocumentsUI/res/drawable/ic_root_sdcard.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_root_sdcard_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml b/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml
new file mode 100644
index 0000000..3021b16
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_root_smartphone.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17 1.01L7 1c-1.1 0,-2 .9,-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2,-.9 2,-2V3c0,-1.1,-.9,-1.99,-2,-1.99zM17 19H7V5h10v14z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index fe06eaf..ecc26e1 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!--
+     Copyright (C) 2013 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -18,8 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@color/item_doc_background"
-    android:orientation="horizontal"
-    android:focusable="true">
+    android:focusable="true"
+    android:orientation="horizontal" >
 
     <View
         android:id="@+id/focus_indicator"
@@ -29,71 +30,76 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/list_item_height"
-        android:paddingStart="@dimen/list_item_padding"
-        android:paddingEnd="@dimen/list_item_padding"
+        android:baselineAligned="false"
         android:gravity="center_vertical"
+        android:minHeight="@dimen/list_item_height"
         android:orientation="horizontal"
-        android:baselineAligned="false">
+        android:paddingEnd="@dimen/list_item_padding"
+        android:paddingStart="@dimen/list_item_padding" >
 
         <FrameLayout
             android:id="@android:id/icon"
-            android:layout_width="@dimen/icon_size"
-            android:layout_height="@dimen/icon_size"
-            android:layout_marginStart="0dp"
-            android:layout_marginEnd="16dp">
+            android:layout_width="@dimen/list_item_thumbnail_size"
+            android:layout_height="@dimen/list_item_thumbnail_size"
+            android:layout_marginEnd="16dp"
+            android:layout_marginStart="0dp" >
 
             <ImageView
                 android:id="@+id/icon_mime"
                 android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:scaleType="centerInside"
-                android:contentDescription="@null" />
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:contentDescription="@null"
+                android:scaleType="centerInside" />
 
             <ImageView
                 android:id="@+id/icon_thumb"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:scaleType="centerCrop"
-                android:contentDescription="@null" />
+                android:layout_gravity="center"
+                android:contentDescription="@null"
+                android:scaleType="centerCrop" />
+
+            <ImageView
+                android:id="@+id/icon_check"
+                android:layout_width="@dimen/check_icon_size"
+                android:layout_height="@dimen/check_icon_size"
+                android:layout_gravity="center"
+                android:alpha="0"
+                android:contentDescription="@null"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_check_circle" />
 
         </FrameLayout>
 
         <!-- This is the one special case where we want baseline alignment! -->
+
         <LinearLayout
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:orientation="horizontal">
+            android:orientation="horizontal" >
 
             <TextView
                 android:id="@android:id/title"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_weight="0.5"
                 android:layout_marginEnd="12dp"
-                android:singleLine="true"
+                android:layout_weight="0.5"
                 android:ellipsize="middle"
+                android:singleLine="true"
                 android:textAlignment="viewStart"
                 android:textAppearance="@android:style/TextAppearance.Material.Subhead"
                 android:textColor="?android:attr/textColorPrimary" />
 
-            <ImageView
-                android:id="@android:id/icon1"
-                android:layout_width="@dimen/root_icon_size"
-                android:layout_height="@dimen/root_icon_size"
-                android:layout_marginEnd="8dp"
-                android:scaleType="centerInside"
-                android:contentDescription="@null" />
-
             <TextView
                 android:id="@android:id/summary"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_weight="0.25"
                 android:layout_marginEnd="12dp"
-                android:singleLine="true"
+                android:layout_weight="0.25"
                 android:ellipsize="end"
+                android:singleLine="true"
                 android:textAlignment="viewStart"
                 android:textAppearance="@android:style/TextAppearance.Material.Body1"
                 android:textColor="?android:attr/textColorSecondary" />
@@ -102,11 +108,11 @@
                 android:id="@+id/size"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_weight="0.125"
                 android:layout_marginEnd="12dp"
+                android:layout_weight="0.125"
+                android:ellipsize="end"
                 android:minWidth="70dp"
                 android:singleLine="true"
-                android:ellipsize="end"
                 android:textAlignment="viewEnd"
                 android:textAppearance="@android:style/TextAppearance.Material.Body1"
                 android:textColor="?android:attr/textColorSecondary" />
@@ -115,17 +121,15 @@
                 android:id="@+id/date"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_weight="0.125"
                 android:layout_marginEnd="12dp"
+                android:layout_weight="0.125"
+                android:ellipsize="end"
                 android:minWidth="70dp"
                 android:singleLine="true"
-                android:ellipsize="end"
                 android:textAlignment="viewEnd"
                 android:textAppearance="@android:style/TextAppearance.Material.Body1"
                 android:textColor="?android:attr/textColorSecondary" />
-
         </LinearLayout>
-
     </LinearLayout>
 
 </com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index f9bbccb..1b5911d 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -23,11 +23,11 @@
     <ProgressBar
         android:id="@+id/progressbar"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/progress_bar_height"
         android:indeterminate="true"
         style="@style/TrimmedHorizontalProgressBar"
         android:visibility="gone"/>
-  
+
     <FrameLayout
         android:id="@+id/container_message_bar"
         android:layout_width="match_parent"
@@ -44,7 +44,7 @@
         android:layout_height="match_parent"
         android:orientation="vertical"
         android:visibility="gone">
-        
+
         <TextView
             android:id="@+id/message"
             android:layout_width="wrap_content"
@@ -58,9 +58,9 @@
             android:layout_height="wrap_content"
             android:text="@string/button_retry"
             style="?android:attr/buttonBarPositiveButtonStyle" />
-        
+
     </LinearLayout>
-    
+
     <!-- This FrameLayout works around b/24189541 -->
     <FrameLayout
         android:layout_width="match_parent"
@@ -68,6 +68,7 @@
 
         <android.support.v7.widget.RecyclerView
             android:id="@+id/list"
+            android:background="@color/window_background"
             android:scrollbars="vertical"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/res/layout/item_dir_grid.xml b/packages/DocumentsUI/res/layout/item_dir_grid.xml
index c17b4c8..a08e029 100644
--- a/packages/DocumentsUI/res/layout/item_dir_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_dir_grid.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!--
+     Copyright (C) 2013 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -20,31 +21,49 @@
     android:layout_margin="@dimen/grid_item_margin"
     android:background="@color/item_doc_background"
     android:elevation="5dp"
-    android:focusable="true">
+    android:focusable="true" >
 
     <LinearLayout
-      android:layout_width="match_parent"
-      android:layout_height="match_parent"
-      android:orientation="horizontal"
-      android:paddingTop="16dp"
-      android:paddingBottom="16dp"
-      android:paddingLeft="12dp"
-      android:paddingRight="12dp">
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:paddingBottom="16dp"
+        android:paddingLeft="12dp"
+        android:paddingRight="12dp"
+        android:paddingTop="16dp"
+        android:gravity="center_vertical">
 
-        <ImageView
-            android:src="@drawable/ic_doc_folder"
+        <FrameLayout
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginEnd="8dp"
-            android:scaleType="centerInside"
-            android:contentDescription="@null"/>
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="8dp" >
+
+            <ImageView
+                android:id="@+id/icon_mime_sm"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:contentDescription="@null"
+                android:scaleType="centerInside"
+                android:src="@drawable/ic_doc_folder" />
+
+            <ImageView
+                android:id="@+id/icon_check"
+                android:layout_width="@dimen/check_icon_size"
+                android:layout_height="@dimen/check_icon_size"
+                android:alpha="0"
+                android:contentDescription="@null"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_check_circle" />
+
+        </FrameLayout>
 
         <TextView
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:singleLine="true"
             android:ellipsize="middle"
+            android:singleLine="true"
             android:textAlignment="viewStart"
             android:textAppearance="@android:style/TextAppearance.Material.Subhead"
             android:textColor="@*android:color/primary_text_default_material_light" />
@@ -52,11 +71,12 @@
     </LinearLayout>
 
     <!-- An overlay that draws the item border when it is focused. -->
+
     <View
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:contentDescription="@null"
         android:background="@drawable/item_doc_grid_border"
+        android:contentDescription="@null"
         android:duplicateParentState="true" />
 
 </FrameLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index c0fc2c3..dd02d1c 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -39,10 +39,11 @@
             android:contentDescription="@null" />
 
         <com.android.documentsui.GridItemThumbnail
-            android:id="@+id/icon_mime"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:scaleType="centerInside"
+            android:id="@+id/icon_mime_lg"
+            android:layout_width="@dimen/icon_size"
+            android:layout_height="@dimen/icon_size"
+            android:layout_gravity="center"
+            android:scaleType="fitCenter"
             android:contentDescription="@null" />
 
     </FrameLayout>
@@ -61,13 +62,25 @@
         android:paddingRight="12dp">
 
         <ImageView
-            android:id="@android:id/icon1"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
+            android:id="@+id/icon_mime_sm"
+            android:layout_width="@dimen/grid_item_icon_size"
+            android:layout_height="@dimen/grid_item_icon_size"
             android:layout_marginEnd="8dp"
             android:layout_alignParentStart="true"
             android:layout_centerVertical="true"
-            android:scaleType="centerInside"
+            android:scaleType="center"
+            android:contentDescription="@null"/>
+
+        <ImageView
+            android:id="@+id/icon_check"
+            android:src="@drawable/ic_check_circle"
+            android:alpha="0"
+            android:layout_width="@dimen/check_icon_size"
+            android:layout_height="@dimen/check_icon_size"
+            android:layout_marginEnd="8dp"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"
+            android:scaleType="fitCenter"
             android:contentDescription="@null"/>
 
         <TextView
@@ -75,7 +88,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
-            android:layout_toEndOf="@android:id/icon1"
+            android:layout_toEndOf="@id/icon_mime_sm"
             android:singleLine="true"
             android:ellipsize="middle"
             android:textAlignment="viewStart"
@@ -86,7 +99,7 @@
             android:id="@+id/size"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_toEndOf="@android:id/icon1"
+            android:layout_toEndOf="@id/icon_mime_sm"
             android:layout_below="@android:id/title"
             android:layout_marginEnd="4dp"
             android:singleLine="true"
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index e068423..8d98377 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!--
+     Copyright (C) 2013 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -18,8 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@color/item_doc_background"
-    android:orientation="horizontal"
-    android:focusable="true">
+    android:focusable="true"
+    android:orientation="horizontal" >
 
     <View
         android:id="@+id/focus_indicator"
@@ -29,82 +30,76 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/list_item_height"
-        android:paddingStart="@dimen/list_item_padding"
-        android:paddingEnd="@dimen/list_item_padding"
+        android:baselineAligned="false"
         android:gravity="center_vertical"
+        android:minHeight="@dimen/list_item_height"
         android:orientation="horizontal"
-        android:baselineAligned="false">
+        android:paddingEnd="@dimen/list_item_padding"
+        android:paddingStart="@dimen/list_item_padding" >
 
         <FrameLayout
             android:id="@android:id/icon"
-            android:layout_width="@dimen/icon_size"
-            android:layout_height="@dimen/icon_size"
-            android:layout_marginEnd="16dp">
+            android:layout_width="@dimen/list_item_thumbnail_size"
+            android:layout_height="@dimen/list_item_thumbnail_size"
+            android:layout_marginEnd="16dp" >
 
             <ImageView
                 android:id="@+id/icon_mime"
                 android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:scaleType="centerInside"
-                android:contentDescription="@null" />
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:contentDescription="@null"
+                android:scaleType="centerInside" />
 
             <ImageView
                 android:id="@+id/icon_thumb"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:scaleType="centerCrop"
-                android:contentDescription="@null" />
+                android:contentDescription="@null"
+                android:scaleType="centerCrop" />
 
+            <ImageView
+                android:id="@+id/icon_check"
+                android:layout_width="@dimen/check_icon_size"
+                android:layout_height="@dimen/check_icon_size"
+                android:layout_gravity="center"
+                android:alpha="0"
+                android:contentDescription="@null"
+                android:scaleType="fitCenter"
+                android:src="@drawable/ic_check_circle" />
         </FrameLayout>
 
         <LinearLayout
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:orientation="vertical">
+            android:orientation="vertical" >
 
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                android:baselineAligned="false">
-
-                <TextView
-                    android:id="@android:id/title"
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:singleLine="true"
-                    android:ellipsize="middle"
-                    android:textAlignment="viewStart"
-                    android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-                    android:textColor="?android:attr/textColorPrimary" />
-
-                <ImageView
-                    android:id="@android:id/icon1"
-                    android:layout_width="@dimen/root_icon_size"
-                    android:layout_height="@dimen/root_icon_size"
-                    android:layout_marginStart="8dp"
-                    android:scaleType="centerInside"
-                    android:contentDescription="@null" />
-
-            </LinearLayout>
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:ellipsize="middle"
+                android:singleLine="true"
+                android:textAlignment="viewStart"
+                android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                android:textColor="?android:attr/textColorPrimary" />
 
             <LinearLayout
                 android:id="@+id/line2"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:baselineAligned="false"
                 android:gravity="center_vertical"
-                android:orientation="horizontal"
-                android:baselineAligned="false">
+                android:orientation="horizontal" >
 
                 <TextView
                     android:id="@+id/date"
                     android:layout_width="90dp"
                     android:layout_height="wrap_content"
-                    android:singleLine="true"
                     android:ellipsize="end"
+                    android:singleLine="true"
                     android:textAlignment="viewStart"
                     android:textAppearance="@android:style/TextAppearance.Material.Body1"
                     android:textColor="?android:attr/textColorSecondary" />
@@ -114,8 +109,8 @@
                     android:layout_width="90dp"
                     android:layout_height="wrap_content"
                     android:layout_marginStart="8dp"
-                    android:singleLine="true"
                     android:ellipsize="end"
+                    android:singleLine="true"
                     android:textAlignment="viewStart"
                     android:textAppearance="@android:style/TextAppearance.Material.Body1"
                     android:textColor="?android:attr/textColorSecondary" />
@@ -124,18 +119,15 @@
                     android:id="@android:id/summary"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
-                    android:layout_weight="1"
                     android:layout_marginStart="8dp"
-                    android:singleLine="true"
+                    android:layout_weight="1"
                     android:ellipsize="end"
+                    android:singleLine="true"
                     android:textAlignment="viewStart"
                     android:textAppearance="@android:style/TextAppearance.Material.Body1"
                     android:textColor="?android:attr/textColorSecondary" />
-
             </LinearLayout>
-
         </LinearLayout>
-
     </LinearLayout>
 
 </com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 7e0649b..a3cfde8 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -15,11 +15,19 @@
 -->
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
+<!-- showAsAction flag impacts the behavior of SearchView.
+     When set to collapseActionView, collapsing SearchView to icon is the
+     default behavior. It would fit UX, however after expanding SearchView is
+     shown on the left site of the toolbar (replacing title). Since no way to
+     prevent this behavior was found, the flag is set to always. SearchView is
+     always visible by default and it is being collapse manually by calling
+     setIconified() method
+-->
     <item
         android:id="@+id/menu_search"
         android:title="@string/menu_search"
         android:icon="@drawable/ic_menu_search"
-        android:showAsAction="always|collapseActionView"
+        android:showAsAction="always"
         android:actionViewClass="android.widget.SearchView"
         android:imeOptions="actionSearch" />
     <item
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index b522e0a..c303e0c 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -97,8 +97,8 @@
     <string name="copy_failure_alert_content" msgid="3715575000297709082">"Faili hizi hazikunakiliwa: <xliff:g id="LIST">%1$s</xliff:g>"</string>
     <string name="move_failure_alert_content" msgid="7151140279020481180">"Faili hizi hazikuhamishwa: <xliff:g id="LIST">%1$s</xliff:g>"</string>
     <plurals name="clipboard_files_clipped" formatted="false" msgid="855459017537058539">
-      <item quantity="other">Alinakili faili <xliff:g id="COUNT_1">%1$d</xliff:g> kwenye ubaoklipu.</item>
-      <item quantity="one">Alinakili faili <xliff:g id="COUNT_0">%1$d</xliff:g> kwenye ubaoklipu.</item>
+      <item quantity="other">Alinakili faili <xliff:g id="COUNT_1">%1$d</xliff:g> kwenye ubao wa kunakili.</item>
+      <item quantity="one">Alinakili faili <xliff:g id="COUNT_0">%1$d</xliff:g> kwenye ubao wa kunakili.</item>
     </plurals>
     <string name="clipboard_files_cannot_paste" msgid="2878324825602325706">"Haiwezi kubandika faili zilizochaguliwa katika eneo hili."</string>
 </resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 153c673..c868d34 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -33,4 +33,6 @@
     <color name="item_doc_background">#fffafafa</color>
     <color name="item_doc_background_selected">#ffe0f2f1</color>
 
+    <color name="menu_search_background">#ff676f74</color>
+
 </resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index 060871d..5adb165 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -15,9 +15,18 @@
 -->
 
 <resources>
+    <dimen name="grid_container_padding">10dp</dimen>
+    <dimen name="list_container_padding">0dp</dimen>
+
     <dimen name="icon_size">40dp</dimen>
     <dimen name="root_icon_size">24dp</dimen>
     <dimen name="root_icon_margin">0dp</dimen>
+    <dimen name="check_icon_size">30dp</dimen>
+
+    <dimen name="list_item_thumbnail_size">40dp</dimen>
+    <dimen name="grid_item_icon_size">30dp</dimen>
+
+    <dimen name="progress_bar_height">4dp</dimen>
 
     <dimen name="grid_width">152dp</dimen>
     <dimen name="grid_height">176dp</dimen>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 9c0a04c..be54496 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -38,13 +38,14 @@
 import android.provider.DocumentsContract.Root;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
+import android.text.TextUtils;
 import android.util.Log;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
@@ -60,6 +61,7 @@
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
+import com.android.internal.util.Preconditions;
 
 import libcore.io.IoUtils;
 
@@ -111,6 +113,13 @@
         setContentView(mLayoutId);
 
         mRoots = DocumentsApplication.getRootsCache(this);
+        mRoots.setOnCacheUpdateListener(
+                new RootsCache.OnCacheUpdateListener() {
+                    @Override
+                    public void onCacheUpdate() {
+                        new HandleRootsChangedTask().execute(getCurrentRoot());
+                    }
+                });
         mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
         mSearchManager = new SearchManager();
 
@@ -167,6 +176,12 @@
         return true;
     }
 
+    @Override
+    protected void onDestroy() {
+        mRoots.setOnCacheUpdateListener(null);
+        super.onDestroy();
+    }
+
     State buildDefaultState() {
         State state = new State();
 
@@ -191,10 +206,7 @@
 
     void onRootPicked(RootInfo root) {
         // Clear entire backstack and start in new root
-        mState.stack.root = root;
-        mState.stack.clear();
-        mState.stackTouched = true;
-
+        mState.onRootChanged(root);
         mSearchManager.update(root);
 
         // Recents is always in memory, so we just load it directly.
@@ -203,7 +215,7 @@
         if (mRoots.isRecentsRoot(root)) {
             onCurrentDirectoryChanged(ANIM_SIDE);
         } else {
-            new PickRootTask(root).executeOnExecutor(getExecutorForCurrentDirectory());
+            new PickRootTask(root, true).executeOnExecutor(getExecutorForCurrentDirectory());
         }
     }
 
@@ -214,6 +226,7 @@
                 case R.id.menu_advanced:
                 case R.id.menu_file_size:
                 case R.id.menu_new_window:
+                case R.id.menu_search:
                     break;
                 default:
                     item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -305,8 +318,7 @@
 
     void openContainerDocument(DocumentInfo doc) {
         checkArgument(doc.isContainer());
-        mState.stack.push(doc);
-        mState.stackTouched = true;
+        mState.pushDocument(doc);
         onCurrentDirectoryChanged(ANIM_DOWN);
     }
 
@@ -315,6 +327,8 @@
      * the (abstract) directoryChanged method will be called.
      * @param anim
      */
+    // TODO: Refactor the usage of the method - now it is called not only when the directory
+    // changed, but also to refresh the content of the directory while searching
     final void onCurrentDirectoryChanged(int anim) {
         mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
         onDirectoryChanged(anim);
@@ -325,7 +339,11 @@
         }
 
         updateActionBar();
-        invalidateOptionsMenu();
+
+        // Prevents searchView from being recreated while searching
+        if (!mSearchManager.isSearching()) {
+            invalidateOptionsMenu();
+        }
     }
 
     final List<String> getExcludedAuthorities() {
@@ -450,7 +468,7 @@
             return;
         }
 
-        if (!mState.stackTouched) {
+        if (!mState.hasLocationChanged()) {
             super.onBackPressed();
             return;
         }
@@ -471,9 +489,7 @@
         try {
             // Update the restored stack to ensure we have freshest data
             stack.updateDocuments(getContentResolver());
-
-            mState.stack = stack;
-            mState.stackTouched = true;
+            mState.setStack(stack);
             onCurrentDirectoryChanged(ANIM_SIDE);
 
         } catch (FileNotFoundException e) {
@@ -481,31 +497,35 @@
         }
     }
 
+    private DocumentInfo getRootDocumentBlocking(RootInfo root) {
+        try {
+            final Uri uri = DocumentsContract.buildDocumentUri(
+                    root.authority, root.documentId);
+            return DocumentInfo.fromUri(getContentResolver(), uri);
+        } catch (FileNotFoundException e) {
+            Log.w(mTag, "Failed to find root", e);
+            return null;
+        }
+    }
+
     final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
         private RootInfo mRoot;
+        private boolean mTouched;
 
-        public PickRootTask(RootInfo root) {
+        public PickRootTask(RootInfo root, boolean touched) {
             mRoot = root;
+            mTouched = touched;
         }
 
         @Override
         protected DocumentInfo doInBackground(Void... params) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(
-                        mRoot.authority, mRoot.documentId);
-                return DocumentInfo.fromUri(getContentResolver(), uri);
-            } catch (FileNotFoundException e) {
-                Log.w(mTag, "Failed to find root", e);
-                return null;
-            }
+            return getRootDocumentBlocking(mRoot);
         }
 
         @Override
         protected void onPostExecute(DocumentInfo result) {
-            if (result != null) {
-                mState.stack.push(result);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
+            if (result != null && !isDestroyed()) {
+                openContainerDocument(result);
             }
         }
     }
@@ -591,6 +611,40 @@
         }
     }
 
+    final class HandleRootsChangedTask extends AsyncTask<RootInfo, Void, RootInfo> {
+        DocumentInfo mHome;
+
+        @Override
+        protected RootInfo doInBackground(RootInfo... roots) {
+            checkArgument(roots.length == 1);
+            final RootInfo currentRoot = roots[0];
+            final Collection<RootInfo> cachedRoots = mRoots.getRootsBlocking();
+            RootInfo homeRoot = null;
+            for (final RootInfo root : cachedRoots) {
+                if (root.isHome()) {
+                    homeRoot = root;
+                }
+                if (root.getUri().equals(currentRoot.getUri())) {
+                    // We don't need to change the current root as the current root was not removed.
+                    return null;
+                }
+            }
+            Preconditions.checkNotNull(homeRoot);
+            mHome = getRootDocumentBlocking(homeRoot);
+            return homeRoot;
+        }
+
+        @Override
+        protected void onPostExecute(RootInfo homeRoot) {
+            if (homeRoot != null && mHome != null && !isDestroyed()) {
+                // Clear entire backstack and start in new root
+                mState.onRootChanged(homeRoot);
+                mSearchManager.update(homeRoot);
+                openContainerDocument(mHome);
+            }
+        }
+    }
+
     final class ItemSelectedListener implements OnItemSelectedListener {
 
         boolean mIgnoreNextNavigation;
@@ -603,8 +657,7 @@
             }
 
             while (mState.stack.size() > position + 1) {
-                mState.stackTouched = true;
-                mState.stack.pop();
+                mState.popDocument();
             }
             onCurrentDirectoryChanged(ANIM_UP);
         }
@@ -682,7 +735,7 @@
      * Facade over the various search parts in the menu.
      */
     final class SearchManager implements
-            SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener,
+            SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
             DocumentsToolBar.OnActionViewCollapsedListener {
 
         private boolean mSearchExpanded;
@@ -700,9 +753,10 @@
             mView = (SearchView) mMenu.getActionView();
 
             mActionBar.setOnActionViewCollapsedListener(this);
-            mMenu.setOnActionExpandListener(this);
             mView.setOnQueryTextListener(this);
             mView.setOnCloseListener(this);
+            mView.setOnSearchClickListener(this);
+            mView.setOnQueryTextFocusChangeListener(this);
         }
 
         /**
@@ -755,19 +809,13 @@
          *     search currently.
          */
         boolean cancelSearch() {
-            boolean collapsed = false;
-            boolean closed = false;
-
-            if (mActionBar.hasExpandedActionView()) {
-                mActionBar.collapseActionView();
-                collapsed = true;
-            }
-
             if (isExpanded() || isSearching()) {
-                onClose();
-                closed = true;
+                // If the query string is not empty search view won't get iconified
+                mView.setQuery("", false);
+                mView.setIconified(true);
+                return true;
             }
-            return collapsed || closed;
+            return false;
         }
 
         boolean isSearching() {
@@ -778,6 +826,11 @@
             return mSearchExpanded;
         }
 
+        /**
+         * Clears the search.
+         * @return True if the default behavior of clearing/dismissing SearchView should be
+         *      overridden. False otherwise.
+         */
         @Override
         public boolean onClose() {
             mSearchExpanded = false;
@@ -786,33 +839,33 @@
                 return false;
             }
 
-            mState.currentSearch = null;
-            onCurrentDirectoryChanged(ANIM_NONE);
+            mView.setBackgroundColor(
+                    getResources().getColor(android.R.color.transparent, null));
+
+            // Refresh the directory if a search was done
+            if(mState.currentSearch != null) {
+                mState.currentSearch = null;
+                onCurrentDirectoryChanged(ANIM_NONE);
+            }
+
             return false;
         }
 
+        /**
+         * Sets mSearchExpanded.
+         * Called when search icon is clicked to start search.
+         * Used to detect when the view expanded instead of onMenuItemActionExpand, because
+         * SearchView has showAsAction set to always and onMenuItemAction* methods are not called.
+         */
         @Override
-        public boolean onMenuItemActionExpand(MenuItem item) {
+        public void onClick (View v) {
             mSearchExpanded = true;
-            updateActionBar();
-            return true;
-        }
-
-        @Override
-        public boolean onMenuItemActionCollapse(MenuItem item) {
-            mSearchExpanded = false;
-            if (mIgnoreNextCollapse) {
-                mIgnoreNextCollapse = false;
-                return true;
-            }
-            mState.currentSearch = null;
-            onCurrentDirectoryChanged(ANIM_NONE);
-            return true;
+            mView.setBackgroundColor(
+                    getResources().getColor(R.color.menu_search_background, null));
         }
 
         @Override
         public boolean onQueryTextSubmit(String query) {
-            mSearchExpanded = true;
             mState.currentSearch = query;
             mView.clearFocus();
             onCurrentDirectoryChanged(ANIM_NONE);
@@ -825,6 +878,18 @@
         }
 
         @Override
+        public void onFocusChange(View v, boolean hasFocus) {
+            if(!hasFocus) {
+                if(mState.currentSearch == null) {
+                    mView.setIconified(true);
+                }
+                else if(TextUtils.isEmpty(mView.getQuery())) {
+                    cancelSearch();
+                }
+            }
+        }
+
+        @Override
         public void onActionViewCollapsed() {
             updateActionBar();
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
deleted file mode 100644
index 6a5911b..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * Copyright (C) 2015 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.documentsui;
-
-import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.model.DocumentInfo.getCursorLong;
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
-import android.app.Activity;
-import android.app.IntentService;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.widget.Snackbar;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.webkit.MimeTypeMap;
-
-import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.model.RootInfo;
-
-import libcore.io.IoUtils;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-public class CopyService extends IntentService {
-    public static final String TAG = "CopyService";
-
-    private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
-    public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
-    public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
-    public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE";
-
-    public static final int TRANSFER_MODE_COPY = 1;
-    public static final int TRANSFER_MODE_MOVE = 2;
-
-    // TODO: Move it to a shared file when more operations are implemented.
-    public static final int FAILURE_COPY = 1;
-
-    // Parameters of the copy job. Requests to an IntentService are serialized so this code only
-    // needs to deal with one job at a time.
-    // NOTE: This must be declared by concrete type as the concrete type
-    // is required by putParcelableArrayListExtra.
-    private final ArrayList<DocumentInfo> mFailedFiles = new ArrayList<>();
-
-    private PowerManager mPowerManager;
-
-    private NotificationManager mNotificationManager;
-    private Notification.Builder mProgressBuilder;
-
-    // Jobs are serialized but a job ID is used, to avoid mixing up cancellation requests.
-    private String mJobId;
-    private volatile boolean mIsCancelled;
-    private long mBatchSize;
-    private long mBytesCopied;
-    private long mStartTime;
-    private long mLastNotificationTime;
-    // Speed estimation
-    private long mBytesCopiedSample;
-    private long mSampleTime;
-    private long mSpeed;
-    private long mRemainingTime;
-    // Provider clients are acquired for the duration of each copy job. Note that there is an
-    // implicit assumption that all srcs come from the same authority.
-    private ContentProviderClient mSrcClient;
-    private ContentProviderClient mDstClient;
-
-    // For testing only.
-    @Nullable private TestOnlyListener mJobFinishedListener;
-
-    public CopyService() {
-        super("CopyService");
-    }
-
-    /**
-     * Starts the service for a copy operation.
-     *
-     * @param context Context for the intent.
-     * @param srcDocs A list of src files to copy.
-     * @param dstStack The copy destination stack.
-     */
-    public static void start(Activity activity, List<DocumentInfo> srcDocs, DocumentStack dstStack,
-            int mode) {
-        final Resources res = activity.getResources();
-        final Intent copyIntent = new Intent(activity, CopyService.class);
-        copyIntent.putParcelableArrayListExtra(
-                EXTRA_SRC_LIST,
-                // Don't create a copy unless absolutely necessary :)
-                srcDocs instanceof ArrayList
-                    ? (ArrayList<DocumentInfo>) srcDocs
-                    : new ArrayList<DocumentInfo>(srcDocs));
-        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack);
-        copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode);
-
-        int toastMessage = (mode == TRANSFER_MODE_COPY) ? R.plurals.copy_begin
-                : R.plurals.move_begin;
-        Snackbars.makeSnackbar(activity,
-                res.getQuantityString(toastMessage, srcDocs.size(), srcDocs.size()),
-                Snackbar.LENGTH_SHORT).show();
-        activity.startService(copyIntent);
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (intent.hasExtra(EXTRA_CANCEL)) {
-            handleCancel(intent);
-        }
-        return super.onStartCommand(intent, flags, startId);
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        if (intent.hasExtra(EXTRA_CANCEL)) {
-            handleCancel(intent);
-            return;
-        }
-
-        final PowerManager.WakeLock wakeLock = mPowerManager
-                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
-        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
-        // Copy by default.
-        final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY);
-
-        try {
-            wakeLock.acquire();
-
-            // Acquire content providers.
-            mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
-                    srcs.get(0).authority);
-            mDstClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
-                    stack.peek().authority);
-
-            setupCopyJob(srcs, stack, transferMode);
-
-            final String opDesc = transferMode == TRANSFER_MODE_COPY ? "copy" : "move";
-            DocumentInfo srcInfo;
-            DocumentInfo dstInfo;
-            for (int i = 0; i < srcs.size() && !mIsCancelled; ++i) {
-                srcInfo = srcs.get(i);
-                dstInfo = stack.peek();
-
-                // Guard unsupported recursive operation.
-                if (dstInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstInfo)) {
-                    if (DEBUG) Log.d(TAG,
-                            "Skipping recursive " + opDesc + " of directory " + dstInfo.derivedUri);
-                    mFailedFiles.add(srcInfo);
-                    continue;
-                }
-
-                if (DEBUG) Log.d(TAG,
-                        "Performing " + opDesc + " of " + srcInfo.displayName
-                        + " (" + srcInfo.derivedUri + ")" + " to " + dstInfo.displayName
-                        + " (" + dstInfo.derivedUri + ")");
-
-                copy(srcInfo, dstInfo, transferMode);
-            }
-        } catch (Exception e) {
-            // Catch-all to prevent any copy errors from wedging the app.
-            Log.e(TAG, "Exceptions occurred during copying", e);
-        } finally {
-            if (DEBUG) Log.d(TAG, "Cleaning up after copy");
-            ContentProviderClient.releaseQuietly(mSrcClient);
-            ContentProviderClient.releaseQuietly(mDstClient);
-
-            wakeLock.release();
-
-            // Dismiss the ongoing copy notification when the copy is done.
-            mNotificationManager.cancel(mJobId, 0);
-
-            if (mFailedFiles.size() > 0) {
-                Log.e(TAG, mFailedFiles.size() + " files failed to copy");
-                final Context context = getApplicationContext();
-                final Intent navigateIntent = buildNavigateIntent(context, stack);
-                navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
-                navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
-                navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
-
-                final int titleResourceId = (transferMode == TRANSFER_MODE_COPY ?
-                        R.plurals.copy_error_notification_title :
-                        R.plurals.move_error_notification_title);
-                final Notification.Builder errorBuilder = new Notification.Builder(this)
-                        .setContentTitle(context.getResources().getQuantityString(titleResourceId,
-                                mFailedFiles.size(), mFailedFiles.size()))
-                        .setContentText(getString(R.string.notification_touch_for_details))
-                        .setContentIntent(PendingIntent.getActivity(context, 0, navigateIntent,
-                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT))
-                        .setCategory(Notification.CATEGORY_ERROR)
-                        .setSmallIcon(R.drawable.ic_menu_copy)
-                        .setAutoCancel(true);
-                mNotificationManager.notify(mJobId, 0, errorBuilder.build());
-            }
-
-            if (mJobFinishedListener != null) {
-                mJobFinishedListener.onFinished(mFailedFiles);
-            }
-
-            if (DEBUG) Log.d(TAG, "Done cleaning up");
-        }
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mPowerManager = getSystemService(PowerManager.class);
-        mNotificationManager = getSystemService(NotificationManager.class);
-    }
-
-    /**
-     * Sets up the CopyService to start tracking and sending notifications for the given batch of
-     * files.
-     *
-     * @param srcs A list of src files to copy.
-     * @param stack The copy destination stack.
-     * @param transferMode The mode (i.e. copy, or move)
-     * @throws RemoteException
-     */
-    private void setupCopyJob(ArrayList<DocumentInfo> srcs, DocumentStack stack, int transferMode)
-            throws RemoteException {
-        final boolean copying = (transferMode == TRANSFER_MODE_COPY);
-        // Create an ID for this copy job. Use the timestamp.
-        mJobId = String.valueOf(SystemClock.elapsedRealtime());
-        // Reset the cancellation flag.
-        mIsCancelled = false;
-
-        final Context context = getApplicationContext();
-        final Intent navigateIntent = buildNavigateIntent(context, stack);
-
-        final String contentTitle = getString(copying ? R.string.copy_notification_title
-                : R.string.move_notification_title);
-        mProgressBuilder = new Notification.Builder(this)
-                .setContentTitle(contentTitle)
-                .setContentIntent(PendingIntent.getActivity(context, 0, navigateIntent, 0))
-                .setCategory(Notification.CATEGORY_PROGRESS)
-                .setSmallIcon(R.drawable.ic_menu_copy)
-                .setOngoing(true);
-
-        final Intent cancelIntent = new Intent(this, CopyService.class);
-        cancelIntent.putExtra(EXTRA_CANCEL, mJobId);
-        mProgressBuilder.addAction(R.drawable.ic_cab_cancel,
-                getString(android.R.string.cancel), PendingIntent.getService(this, 0,
-                        cancelIntent,
-                        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT));
-
-        // Send an initial progress notification.
-        final String contentText = getString(copying ? R.string.copy_preparing
-                : R.string.move_preparing);
-        mProgressBuilder.setProgress(0, 0, true); // Indeterminate progress while setting up.
-        mProgressBuilder.setContentText(contentText);
-        mNotificationManager.notify(mJobId, 0, mProgressBuilder.build());
-
-        // Reset batch parameters.
-        mFailedFiles.clear();
-        mBatchSize = calculateFileSizes(srcs);
-        mBytesCopied = 0;
-        mStartTime = SystemClock.elapsedRealtime();
-        mLastNotificationTime = 0;
-        mBytesCopiedSample = 0;
-        mSampleTime = 0;
-        mSpeed = 0;
-        mRemainingTime = 0;
-
-        // TODO: Check preconditions for copy.
-        // - check that the destination has enough space and is writeable?
-        // - check MIME types?
-    }
-
-    /**
-     * Sets a callback to be run when the next run job is finished.
-     * This is test ONLY instrumentation. The alternative is for us to add
-     * broadcast intents SOLELY for the purpose of testing.
-     * @param listener
-     */
-    @VisibleForTesting
-    void addFinishedListener(TestOnlyListener listener) {
-        this.mJobFinishedListener = listener;
-
-    }
-
-    /**
-     * Only used for testing. Is that obvious enough?
-     */
-    @VisibleForTesting
-    interface TestOnlyListener {
-        void onFinished(List<DocumentInfo> failed);
-    }
-
-    /**
-     * Calculates the cumulative size of all the documents in the list. Directories are recursed
-     * into and totaled up.
-     *
-     * @param srcs
-     * @return Size in bytes.
-     * @throws RemoteException
-     */
-    private long calculateFileSizes(List<DocumentInfo> srcs) throws RemoteException {
-        long result = 0;
-        for (DocumentInfo src : srcs) {
-            if (src.isDirectory()) {
-                // Directories need to be recursed into.
-                result += calculateFileSizesHelper(src.derivedUri);
-            } else {
-                result += src.size;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Calculates (recursively) the cumulative size of all the files under the given directory.
-     *
-     * @throws RemoteException
-     */
-    private long calculateFileSizesHelper(Uri uri) throws RemoteException {
-        final String authority = uri.getAuthority();
-        final Uri queryUri = DocumentsContract.buildChildDocumentsUri(authority,
-                DocumentsContract.getDocumentId(uri));
-        final String queryColumns[] = new String[] {
-                Document.COLUMN_DOCUMENT_ID,
-                Document.COLUMN_MIME_TYPE,
-                Document.COLUMN_SIZE
-        };
-
-        long result = 0;
-        Cursor cursor = null;
-        try {
-            cursor = mSrcClient.query(queryUri, queryColumns, null, null, null);
-            while (cursor.moveToNext()) {
-                if (Document.MIME_TYPE_DIR.equals(
-                        getCursorString(cursor, Document.COLUMN_MIME_TYPE))) {
-                    // Recurse into directories.
-                    final Uri subdirUri = DocumentsContract.buildDocumentUri(authority,
-                            getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
-                    result += calculateFileSizesHelper(subdirUri);
-                } else {
-                    // This may return -1 if the size isn't defined. Ignore those cases.
-                    long size = getCursorLong(cursor, Document.COLUMN_SIZE);
-                    result += size > 0 ? size : 0;
-                }
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-
-        return result;
-    }
-
-    /**
-     * Cancels the current copy job, if its ID matches the given ID.
-     *
-     * @param intent The cancellation intent.
-     */
-    private void handleCancel(Intent intent) {
-        final String cancelledId = intent.getStringExtra(EXTRA_CANCEL);
-        // Do nothing if the cancelled ID doesn't match the current job ID. This prevents racey
-        // cancellation requests from affecting unrelated copy jobs.  However, if the current job ID
-        // is null, the service most likely crashed and was revived by the incoming cancel intent.
-        // In that case, always allow the cancellation to proceed.
-        if (Objects.equals(mJobId, cancelledId) || mJobId == null) {
-            // Set the cancel flag. This causes the copy loops to exit.
-            mIsCancelled = true;
-            // Dismiss the progress notification here rather than in the copy loop. This preserves
-            // interactivity for the user in case the copy loop is stalled.
-            mNotificationManager.cancel(cancelledId, 0);
-        }
-    }
-
-    /**
-     * Logs progress on the current copy operation. Displays/Updates the progress notification.
-     *
-     * @param bytesCopied
-     */
-    private void makeProgress(long bytesCopied) {
-        mBytesCopied += bytesCopied;
-        double done = (double) mBytesCopied / mBatchSize;
-        String percent = NumberFormat.getPercentInstance().format(done);
-
-        // Update time estimate
-        long currentTime = SystemClock.elapsedRealtime();
-        long elapsedTime = currentTime - mStartTime;
-
-        // Send out progress notifications once a second.
-        if (currentTime - mLastNotificationTime > 1000) {
-            updateRemainingTimeEstimate(elapsedTime);
-            mProgressBuilder.setProgress(100, (int) (done * 100), false);
-            mProgressBuilder.setContentInfo(percent);
-            if (mRemainingTime > 0) {
-                mProgressBuilder.setContentText(getString(R.string.copy_remaining,
-                        DateUtils.formatDuration(mRemainingTime)));
-            } else {
-                mProgressBuilder.setContentText(null);
-            }
-            mNotificationManager.notify(mJobId, 0, mProgressBuilder.build());
-            mLastNotificationTime = currentTime;
-        }
-    }
-
-    /**
-     * Generates an estimate of the remaining time in the copy.
-     *
-     * @param elapsedTime The time elapsed so far.
-     */
-    private void updateRemainingTimeEstimate(long elapsedTime) {
-        final long sampleDuration = elapsedTime - mSampleTime;
-        final long sampleSpeed = ((mBytesCopied - mBytesCopiedSample) * 1000) / sampleDuration;
-        if (mSpeed == 0) {
-            mSpeed = sampleSpeed;
-        } else {
-            mSpeed = ((3 * mSpeed) + sampleSpeed) / 4;
-        }
-
-        if (mSampleTime > 0 && mSpeed > 0) {
-            mRemainingTime = ((mBatchSize - mBytesCopied) * 1000) / mSpeed;
-        } else {
-            mRemainingTime = 0;
-        }
-
-        mSampleTime = elapsedTime;
-        mBytesCopiedSample = mBytesCopied;
-    }
-
-    /**
-     * Copies a the given documents to the given location.
-     *
-     * @param srcInfo DocumentInfos for the documents to copy.
-     * @param dstDirInfo The destination directory.
-     * @param mode The transfer mode (copy or move).
-     * @return True on success, false on failure.
-     * @throws RemoteException
-     */
-    private boolean copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
-            throws RemoteException {
-        // When copying within the same provider, try to use optimized copying and moving.
-        // If not supported, then fallback to byte-by-byte copy/move.
-        if (srcInfo.authority.equals(dstDirInfo.authority)) {
-            switch (mode) {
-                case TRANSFER_MODE_COPY:
-                    if ((srcInfo.flags & Document.FLAG_SUPPORTS_COPY) != 0) {
-                        if (DocumentsContract.copyDocument(mSrcClient, srcInfo.derivedUri,
-                                dstDirInfo.derivedUri) == null) {
-                            mFailedFiles.add(srcInfo);
-                        }
-                        return false;
-                    }
-                    break;
-                case TRANSFER_MODE_MOVE:
-                    if ((srcInfo.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
-                        if (DocumentsContract.moveDocument(mSrcClient, srcInfo.derivedUri,
-                                dstDirInfo.derivedUri) == null) {
-                            mFailedFiles.add(srcInfo);
-                        }
-                        return false;
-                    }
-                    break;
-                default:
-                    throw new IllegalArgumentException("Unknown transfer mode.");
-            }
-        }
-
-        final String dstMimeType;
-        final String dstDisplayName;
-
-        // If the file is virtual, but can be converted to another format, then try to copy it
-        // as such format. Also, append an extension for the target mime type (if known).
-        if (srcInfo.isVirtualDocument()) {
-            if (!srcInfo.isTypedDocument()) {
-                // Impossible to copy a file which is virtual, but not typed.
-                mFailedFiles.add(srcInfo);
-                return false;
-            }
-            final String[] streamTypes = getContentResolver().getStreamTypes(
-                    srcInfo.derivedUri, "*/*");
-            if (streamTypes != null && streamTypes.length > 0) {
-                dstMimeType = streamTypes[0];
-                final String extension = MimeTypeMap.getSingleton().
-                        getExtensionFromMimeType(dstMimeType);
-                dstDisplayName = srcInfo.displayName +
-                        (extension != null ? "." + extension : srcInfo.displayName);
-            } else {
-                // The provider says that it supports typed documents, but doesn't say
-                // anything about available formats.
-                // TODO: Log failures. b/26192412
-                mFailedFiles.add(srcInfo);
-                return false;
-            }
-        } else {
-            dstMimeType = srcInfo.mimeType;
-            dstDisplayName = srcInfo.displayName;
-        }
-
-        // Create the target document (either a file or a directory), then copy recursively the
-        // contents (bytes or children).
-        final Uri dstUri = DocumentsContract.createDocument(mDstClient,
-                dstDirInfo.derivedUri, dstMimeType, dstDisplayName);
-        if (dstUri == null) {
-            // If this is a directory, the entire subdir will not be copied over.
-            mFailedFiles.add(srcInfo);
-            return false;
-        }
-
-        DocumentInfo dstInfo = null;
-        try {
-            dstInfo = DocumentInfo.fromUri(getContentResolver(), dstUri);
-        } catch (FileNotFoundException e) {
-            mFailedFiles.add(srcInfo);
-            return false;
-        }
-
-        final boolean success;
-        if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
-            success = copyDirectoryHelper(srcInfo, dstInfo, mode);
-        } else {
-            success = copyFileHelper(srcInfo, dstInfo, dstMimeType, mode);
-        }
-
-        if (mode == TRANSFER_MODE_MOVE && success) {
-            // This is racey. We should make sure that we never delete a directory after
-            // it changed, so we don't remove a file which had not been copied earlier
-            // to the target location.
-            try {
-                DocumentsContract.deleteDocument(mSrcClient, srcInfo.derivedUri);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to delete source after moving: " + srcInfo.derivedUri, e);
-                throw e;
-            }
-        }
-
-        return success;
-    }
-
-    /**
-     * Returns true if {@code doc} is a descendant of {@code parentDoc}.
-     * @throws RemoteException
-     */
-    boolean isDescendentOf(DocumentInfo doc, DocumentInfo parentDoc)
-            throws RemoteException {
-        if (parentDoc.isDirectory() && doc.authority.equals(parentDoc.authority)) {
-            return DocumentsContract.isChildDocument(
-                    mDstClient, doc.derivedUri, parentDoc.derivedUri);
-        }
-        return false;
-    }
-
-    /**
-     * Handles recursion into a directory and copying its contents. Note that in linux terms, this
-     * does the equivalent of "cp src/* dst", not "cp -r src dst".
-     *
-     * @param srcDirInfo Info of the directory to copy from. The routine will copy the directory's
-     *            contents, not the directory itself.
-     * @param dstDirInfo Info of the directory to copy to. Must be created beforehand.
-     * @return True on success, false if some of the children failed to copy.
-     * @throws RemoteException
-     */
-    private boolean copyDirectoryHelper(
-            DocumentInfo srcDirInfo, DocumentInfo dstDirInfo, int mode)
-            throws RemoteException {
-        // Recurse into directories. Copy children into the new subdirectory.
-        final String queryColumns[] = new String[] {
-                Document.COLUMN_DISPLAY_NAME,
-                Document.COLUMN_DOCUMENT_ID,
-                Document.COLUMN_MIME_TYPE,
-                Document.COLUMN_SIZE,
-                Document.COLUMN_FLAGS
-        };
-        Cursor cursor = null;
-        boolean success = true;
-        try {
-            // Iterate over srcs in the directory; copy to the destination directory.
-            final Uri queryUri = DocumentsContract.buildChildDocumentsUri(srcDirInfo.authority,
-                    srcDirInfo.documentId);
-            cursor = mSrcClient.query(queryUri, queryColumns, null, null, null);
-            DocumentInfo srcInfo;
-            while (cursor.moveToNext()) {
-                srcInfo = DocumentInfo.fromCursor(cursor, srcDirInfo.authority);
-                success &= copy(srcInfo, dstDirInfo, mode);
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-
-        return success;
-    }
-
-    /**
-     * Handles copying a single file.
-     *
-     * @param srcUriInfo Info of the file to copy from.
-     * @param dstUriInfo Info of the *file* to copy to. Must be created beforehand.
-     * @param mimeType Mime type for the target. Can be different than source for virtual files.
-     * @return True on success, false on error.
-     * @throws RemoteException
-     */
-    private boolean copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, String mimeType,
-            int mode) throws RemoteException {
-        // Copy an individual file.
-        CancellationSignal canceller = new CancellationSignal();
-        ParcelFileDescriptor srcFile = null;
-        ParcelFileDescriptor dstFile = null;
-        InputStream src = null;
-        OutputStream dst = null;
-
-        boolean success = true;
-        try {
-            // If the file is virtual, but can be converted to another format, then try to copy it
-            // as such format.
-            if (srcInfo.isVirtualDocument() && srcInfo.isTypedDocument()) {
-                final AssetFileDescriptor srcFileAsAsset =
-                        mSrcClient.openTypedAssetFileDescriptor(
-                                srcInfo.derivedUri, mimeType, null, canceller);
-                srcFile = srcFileAsAsset.getParcelFileDescriptor();
-                src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
-            } else {
-                srcFile = mSrcClient.openFile(srcInfo.derivedUri, "r", canceller);
-                src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
-            }
-
-            dstFile = mDstClient.openFile(dstInfo.derivedUri, "w", canceller);
-            dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile);
-
-            byte[] buffer = new byte[8192];
-            int len;
-            while ((len = src.read(buffer)) != -1) {
-                if (mIsCancelled) {
-                    success = false;
-                    break;
-                }
-                dst.write(buffer, 0, len);
-                makeProgress(len);
-            }
-
-            srcFile.checkError();
-        } catch (IOException e) {
-            success = false;
-            mFailedFiles.add(srcInfo);
-
-            if (dstFile != null) {
-                try {
-                    dstFile.closeWithError(e.getMessage());
-                } catch (IOException closeError) {
-                    Log.e(TAG, "Error closing destination", closeError);
-                }
-            }
-        } finally {
-            // This also ensures the file descriptors are closed.
-            IoUtils.closeQuietly(src);
-            IoUtils.closeQuietly(dst);
-        }
-
-        if (!success) {
-            // Clean up half-copied files.
-            canceller.cancel();
-            try {
-                DocumentsContract.deleteDocument(mDstClient, dstInfo.derivedUri);
-            } catch (RemoteException e) {
-                // RemoteExceptions usually signal that the connection is dead, so there's no
-                // point attempting to continue. Propagate the exception up so the copy job is
-                // cancelled.
-                Log.w(TAG, "Failed to cleanup after copy error: " + srcInfo.derivedUri, e);
-                throw e;
-            }
-        }
-
-        return success;
-    }
-
-    /**
-     * Creates an intent for navigating back to the destination directory.
-     */
-    private Intent buildNavigateIntent(Context context, DocumentStack stack) {
-        Intent intent = new Intent(context, FilesActivity.class);
-        intent.setAction(DocumentsContract.ACTION_BROWSE);
-        intent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
-        return intent;
-    }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 4eacee5..6719b23 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -114,11 +114,7 @@
         if (userMode != State.MODE_UNKNOWN) {
             result.mode = userMode;
         } else {
-            if ((mDoc.flags & Document.FLAG_DIR_PREFERS_GRID) != 0) {
-                result.mode = State.MODE_GRID;
-            } else {
-                result.mode = State.MODE_LIST;
-            }
+            result.mode = State.MODE_GRID;
         }
 
         if (mUserSortOrder != State.SORT_ORDER_UNKNOWN) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 8ca2cfb..223af89 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -54,6 +54,7 @@
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
+import com.android.documentsui.services.FileOperationService;
 
 import java.util.Arrays;
 import java.util.List;
@@ -154,8 +155,8 @@
         if (state.action == ACTION_PICK_COPY_DESTINATION) {
             state.directoryCopy = intent.getBooleanExtra(
                     Shared.EXTRA_DIRECTORY_COPY, false);
-            state.transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
-                    CopyService.TRANSFER_MODE_COPY);
+            state.transferMode = intent.getIntExtra(FileOperationService.EXTRA_OPERATION,
+                    FileOperationService.OPERATION_COPY);
         }
 
         return state;
@@ -307,8 +308,10 @@
         mSearchManager.showMenu(!picking);
 
         // No display options in recent directories
-        grid.setVisible(!(picking && recents));
-        list.setVisible(!(picking && recents));
+        if (picking && recents) {
+            grid.setVisible(false);
+            list.setVisible(false);
+        }
 
         fileSize.setVisible(fileSize.isVisible() && !picking);
         settings.setVisible(false);
@@ -481,7 +484,7 @@
             // Picking a copy destination is only used internally by us, so we
             // don't need to extend permissions to the caller.
             intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
-            intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode);
+            intent.putExtra(FileOperationService.EXTRA_OPERATION, mState.transferMode);
         } else {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 23074f0..7f6f1c6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
@@ -27,6 +29,8 @@
 
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.services.FileOperationService;
+import com.android.documentsui.services.FileOperations;
 
 import java.util.ArrayList;
 
@@ -37,20 +41,20 @@
         implements DialogInterface.OnClickListener {
     private static final String TAG = "FailureDialogFragment";
 
-    private int mTransferMode;
+    private int mOperationType;
     private ArrayList<DocumentInfo> mFailedSrcList;
 
     public static void show(FragmentManager fm, int failure,
-            ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int transferMode) {
+            ArrayList<DocumentInfo> failedSrcList, DocumentStack dstStack, int operationType) {
         // TODO: Add support for other failures than copy.
-        if (failure != CopyService.FAILURE_COPY) {
+        if (failure != FileOperationService.FAILURE_COPY) {
             return;
         }
 
         final Bundle args = new Bundle();
-        args.putInt(CopyService.EXTRA_FAILURE, failure);
-        args.putInt(CopyService.EXTRA_TRANSFER_MODE, transferMode);
-        args.putParcelableArrayList(CopyService.EXTRA_SRC_LIST, failedSrcList);
+        args.putInt(FileOperationService.EXTRA_FAILURE, failure);
+        args.putInt(FileOperationService.EXTRA_OPERATION, operationType);
+        args.putParcelableArrayList(FileOperationService.EXTRA_SRC_LIST, failedSrcList);
 
         final FragmentTransaction ft = fm.beginTransaction();
         final FailureDialogFragment fragment = new FailureDialogFragment();
@@ -63,10 +67,12 @@
     @Override
     public void onClick(DialogInterface dialog, int whichButton) {
         if (whichButton == DialogInterface.BUTTON_POSITIVE) {
-            CopyService.start(getActivity(), mFailedSrcList,
+            FileOperations.start(
+                    getActivity(),
+                    mFailedSrcList,
                     (DocumentStack) getActivity().getIntent().getParcelableExtra(
                             Shared.EXTRA_STACK),
-                            mTransferMode);
+                    mOperationType);
         }
     }
 
@@ -74,16 +80,27 @@
     public Dialog onCreateDialog(Bundle inState) {
         super.onCreate(inState);
 
-        mTransferMode = getArguments().getInt(CopyService.EXTRA_TRANSFER_MODE);
-        mFailedSrcList = getArguments().getParcelableArrayList(CopyService.EXTRA_SRC_LIST);
+        mOperationType = getArguments().getInt(FileOperationService.EXTRA_OPERATION);
+        mFailedSrcList = getArguments().getParcelableArrayList(FileOperationService.EXTRA_SRC_LIST);
 
         final StringBuilder list = new StringBuilder("<p>");
         for (DocumentInfo documentInfo : mFailedSrcList) {
             list.append(String.format("&#8226; %s<br>", documentInfo.displayName));
         }
         list.append("</p>");
-        final String messageFormat = getString(mTransferMode == CopyService.TRANSFER_MODE_COPY ?
-                R.string.copy_failure_alert_content : R.string.move_failure_alert_content);
+
+        // TODO: Add support for other file operations.
+        checkArgument(
+                mOperationType == FileOperationService.OPERATION_COPY
+                || mOperationType == FileOperationService.OPERATION_MOVE);
+
+        int messageId = mOperationType == FileOperationService.OPERATION_COPY
+                ? R.string.copy_failure_alert_content
+                : R.string.move_failure_alert_content;
+
+        final String messageFormat = getString(
+                messageId);
+
         final String message = String.format(messageFormat, list.toString());
 
         return new AlertDialog.Builder(getActivity())
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index e308f3f..0bd09f6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -49,6 +49,7 @@
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
+import com.android.documentsui.services.FileOperationService;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -120,20 +121,22 @@
                     ProviderExecutor.forAuthority(homeUri.getAuthority()));
         }
 
-        final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
-        final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
-                CopyService.TRANSFER_MODE_COPY);
+        final int failure = intent.getIntExtra(FileOperationService.EXTRA_FAILURE, 0);
+        final int opType = intent.getIntExtra(
+                FileOperationService.EXTRA_OPERATION,
+                FileOperationService.OPERATION_COPY);
+
         // DialogFragment takes care of restoring the dialog on configuration change.
         // Only show it manually for the first time (icicle is null).
         if (icicle == null && failure != 0) {
             final ArrayList<DocumentInfo> failedSrcList =
-                    intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
+                    intent.getParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST);
             FailureDialogFragment.show(
                     getFragmentManager(),
                     failure,
                     failedSrcList,
                     mState.stack,
-                    transferMode);
+                    opType);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index a1213d2..c28fae8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -159,8 +159,8 @@
         add("application/vnd.sun.xml.calc.template", icon);
         add("application/x-kspread", icon);
 
-        // Text
-        icon = R.drawable.ic_doc_text;
+        // Document
+        icon = R.drawable.ic_doc_document;
         add("application/vnd.oasis.opendocument.text", icon);
         add("application/vnd.oasis.opendocument.text-master", icon);
         add("application/vnd.oasis.opendocument.text-template", icon);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index bb6c3b5e..7dac0c1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -107,7 +107,7 @@
                 mAdapter.update(data);
 
                 // When launched into empty recents, show drawer
-                if (mAdapter.isEmpty() && !state.stackTouched &&
+                if (mAdapter.isEmpty() && !state.hasLocationChanged() &&
                         context instanceof DocumentsActivity) {
                     ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 72ee6cbab..21e7566 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -63,6 +63,7 @@
 
     private final Context mContext;
     private final ContentObserver mObserver;
+    private OnCacheUpdateListener mCacheUpdateListener;
 
     private final RootInfo mRecentsRoot = new RootInfo();
 
@@ -94,6 +95,10 @@
         }
     }
 
+    static interface OnCacheUpdateListener {
+        void onCacheUpdate();
+    }
+
     /**
      * Gather roots from all known storage providers.
      */
@@ -209,6 +214,13 @@
             return null;
         }
 
+        @Override
+        protected void onPostExecute(Void result) {
+            if (mCacheUpdateListener != null) {
+                mCacheUpdateListener.onCacheUpdate();
+            }
+        }
+
         private void handleDocumentsProvider(ProviderInfo info) {
             // Ignore stopped packages for now; we might query them
             // later during UI interaction.
@@ -348,6 +360,10 @@
         }
     }
 
+    public void setOnCacheUpdateListener(OnCacheUpdateListener cacheUpdateListener) {
+        mCacheUpdateListener = cacheUpdateListener;
+    }
+
     @VisibleForTesting
     static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) {
         final List<RootInfo> matching = new ArrayList<>();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 4c844c4..beff196 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -297,7 +297,7 @@
 
             for (final RootInfo root : roots) {
                 final RootItem item = new RootItem(root);
-                if (root.isLibrary() || root.isHome()) {
+                if (root.isLibrary()) {
                     libraries.add(item);
                 } else {
                     others.add(item);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index c3366c3..22cb25a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -20,6 +20,9 @@
 import android.text.format.DateUtils;
 import android.text.format.Time;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** @hide */
 public final class Shared {
     /** Intent action name to pick a copy destination. */
@@ -64,4 +67,13 @@
         return DateUtils.formatDateTime(context, when, flags);
     }
 
+    /**
+     * A convenient way to transform any list into a (parcelable) ArrayList.
+     * Uses cast if possible, else creates a new list with entries from {@code list}.
+     */
+    public static <T> ArrayList<T> asArrayList(List<T> list) {
+        return list instanceof ArrayList
+            ? (ArrayList<T>) list
+            : new ArrayList<T>(list);
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index c81d4fb..2f0224f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -24,6 +24,7 @@
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
+import com.android.documentsui.model.RootInfo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -36,7 +37,7 @@
     /** Explicit user choice */
     public int userMode = MODE_UNKNOWN;
     /** Derived after loader */
-    public int derivedMode = MODE_LIST;
+    public int derivedMode = MODE_GRID;
 
     /** Explicit user choice */
     public int userSortOrder = SORT_ORDER_UNKNOWN;
@@ -49,7 +50,6 @@
     public boolean localOnly;
     public boolean forceAdvanced;
     public boolean showAdvanced;
-    public boolean stackTouched;
     public boolean restored;
     public boolean directoryCopy;
     public boolean openableOnly;
@@ -87,6 +87,8 @@
     public static final int SORT_ORDER_LAST_MODIFIED = 2;
     public static final int SORT_ORDER_SIZE = 3;
 
+    private boolean mStackTouched;
+
     public void initAcceptMimes(Intent intent) {
         if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
             acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
@@ -96,6 +98,31 @@
         }
     }
 
+    public void onRootChanged(RootInfo root) {
+        stack.root = root;
+        stack.clear();
+        mStackTouched = true;
+    }
+
+    public void pushDocument(DocumentInfo info) {
+        stack.push(info);
+        mStackTouched = true;
+    }
+
+    public void popDocument() {
+        stack.pop();
+        mStackTouched = true;
+    }
+
+    public void setStack(DocumentStack stack) {
+        this.stack = stack;
+        mStackTouched = true;
+    }
+
+    public boolean hasLocationChanged() {
+        return mStackTouched;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -113,7 +140,6 @@
         out.writeInt(localOnly ? 1 : 0);
         out.writeInt(forceAdvanced ? 1 : 0);
         out.writeInt(showAdvanced ? 1 : 0);
-        out.writeInt(stackTouched ? 1 : 0);
         out.writeInt(restored ? 1 : 0);
         DurableUtils.writeToParcel(out, stack);
         out.writeString(currentSearch);
@@ -121,6 +147,7 @@
         out.writeList(selectedDocumentsForCopy);
         out.writeList(excludedAuthorities);
         out.writeInt(openableOnly ? 1 : 0);
+        out.writeInt(mStackTouched ? 1 : 0);
     }
 
     public static final Creator<State> CREATOR = new Creator<State>() {
@@ -137,7 +164,6 @@
             state.localOnly = in.readInt() != 0;
             state.forceAdvanced = in.readInt() != 0;
             state.showAdvanced = in.readInt() != 0;
-            state.stackTouched = in.readInt() != 0;
             state.restored = in.readInt() != 0;
             DurableUtils.readFromParcel(in, state.stack);
             state.currentSearch = in.readString();
@@ -145,6 +171,7 @@
             in.readList(state.selectedDocumentsForCopy, null);
             in.readList(state.excludedAuthorities, null);
             state.openableOnly = in.readInt() != 0;
+            state.mStackTouched = in.readInt() != 0;
             return state;
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 0d5e34e..22e81c6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -53,6 +53,7 @@
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.LayoutManager;
@@ -77,7 +78,6 @@
 import android.widget.TextView;
 
 import com.android.documentsui.BaseActivity;
-import com.android.documentsui.CopyService;
 import com.android.documentsui.DirectoryLoader;
 import com.android.documentsui.DirectoryResult;
 import com.android.documentsui.DocumentClipper;
@@ -99,18 +99,19 @@
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.RootInfo;
+import com.android.documentsui.services.FileOperationService;
+import com.android.documentsui.services.FileOperations;
+
 import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Display the documents inside a single directory.
  */
-public class DirectoryFragment extends Fragment {
+public class DirectoryFragment extends Fragment implements DocumentsAdapter.Environment {
 
     public static final int TYPE_NORMAL = 1;
     public static final int TYPE_SEARCH = 2;
@@ -126,7 +127,7 @@
     private static final String TAG = "DirectoryFragment";
 
     private static final int LOADER_ID = 42;
-    private static final boolean DEBUG_ENABLE_DND = true;
+    static final boolean DEBUG_ENABLE_DND = true;
 
     private static final String EXTRA_TYPE = "type";
     private static final String EXTRA_ROOT = "root";
@@ -289,7 +290,11 @@
         final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
         final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
 
-        mAdapter = new DocumentsAdapter();
+        mIconHelper = new IconHelper(context, state.derivedMode);
+
+        mAdapter = new SectionBreakDocumentsAdapterWrapper(
+                this, new ModelBackedDocumentsAdapter(this, mIconHelper));
+
         mRecView.setAdapter(mAdapter);
 
         GestureDetector.SimpleOnGestureListener listener =
@@ -328,12 +333,13 @@
         // into the selection manager.
         mSelectionManager = new MultiSelectManager(
                 mRecView,
+                mAdapter,
                 state.allowMultiple
                     ? MultiSelectManager.MODE_MULTIPLE
                     : MultiSelectManager.MODE_SINGLE);
         mSelectionManager.addCallback(new SelectionModeListener());
 
-        mModel = new Model(context, mAdapter);
+        mModel = new Model(context);
         mModel.addUpdateListener(mAdapter);
         mModel.addUpdateListener(mModelUpdateListener);
 
@@ -343,8 +349,6 @@
         mTuner = FragmentTuner.pick(state);
         mClipper = new DocumentClipper(context);
 
-        mIconHelper = new IconHelper(context, state.derivedMode);
-
         boolean hideGridTitles;
         if (mType == TYPE_RECENT_OPEN) {
             // Hide titles when showing recents for picking images/videos
@@ -403,12 +407,11 @@
                     state.derivedMode = result.mode;
                 }
                 state.derivedSortOrder = result.sortOrder;
-                ((BaseActivity) context).onStateChanged();
 
                 updateDisplayState();
 
                 // When launched into empty recents, show drawer
-                if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.stackTouched &&
+                if (mType == TYPE_RECENT_OPEN && mModel.isEmpty() && !state.hasLocationChanged() &&
                         context instanceof DocumentsActivity) {
                     ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
@@ -452,9 +455,15 @@
             return;
         }
 
-        CopyService.start(getActivity(), getDisplayState().selectedDocumentsForCopy,
+        int operationType = data.getIntExtra(
+                FileOperationService.EXTRA_OPERATION,
+                FileOperationService.OPERATION_COPY);
+
+        FileOperations.start(
+                getActivity(),
+                getDisplayState().selectedDocumentsForCopy,
                 (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
-                data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_COPY));
+                operationType);
     }
 
     private boolean onSingleTapUp(MotionEvent e) {
@@ -574,7 +583,10 @@
             case MODE_GRID:
                 if (mGridLayout == null) {
                     mGridLayout = new GridLayoutManager(getContext(), mColumnCount);
-                    mGridLayout.setSpanSizeLookup(mAdapter.createSpanSizeLookup());
+                    SpanSizeLookup lookup = mAdapter.createSpanSizeLookup();
+                    if (lookup != null) {
+                        mGridLayout.setSpanSizeLookup(lookup);
+                    }
                 }
                 layout = mGridLayout;
                 break;
@@ -589,11 +601,11 @@
                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
         }
 
-        mRecView.setLayoutManager(layout);
-        // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of
-        // imperatively calling this function.
-        mSelectionManager.handleLayoutChanged();
+        int pad = getDirectoryPadding(mode);
+        mRecView.setPadding(pad, pad, pad, pad);
         // setting layout manager automatically invalidates existing ViewHolders.
+        mRecView.setLayoutManager(layout);
+        mSelectionManager.handleLayoutChanged();  // RecyclerView doesn't do this for us
         mIconHelper.setMode(mode);
     }
 
@@ -609,6 +621,25 @@
         return columnCount;
     }
 
+    private int getDirectoryPadding(int mode) {
+        switch (mode) {
+            case MODE_GRID:
+                return getResources().getDimensionPixelSize(
+                        R.dimen.grid_container_padding);
+            case MODE_LIST:
+                return getResources().getDimensionPixelSize(
+                        R.dimen.list_container_padding);
+            case MODE_UNKNOWN:
+            default:
+                throw new IllegalArgumentException("Unsupported layout mode: " + mode);
+        }
+    }
+
+    @Override
+    public int getColumnCount() {
+        return mColumnCount;
+    }
+
     /**
      * Manages the integration between our ActionMode and MultiSelectManager, initiating
      * ActionMode when there is a selection, canceling it when there is no selection,
@@ -728,14 +759,14 @@
                     return true;
 
                 case R.id.menu_copy_to:
-                    transferDocuments(selection, CopyService.TRANSFER_MODE_COPY);
+                    transferDocuments(selection, FileOperationService.OPERATION_COPY);
                     mode.finish();
                     return true;
 
                 case R.id.menu_move_to:
                     // Exit selection mode first, so we avoid deselecting deleted documents.
                     mode.finish();
-                    transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
+                    transferDocuments(selection, FileOperationService.OPERATION_MOVE);
                     return true;
 
                 case R.id.menu_copy_to_clipboard:
@@ -887,16 +918,40 @@
                     }
                 }
                 intent.putExtra(Shared.EXTRA_DIRECTORY_COPY, directoryCopy);
-                intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mode);
+                intent.putExtra(FileOperationService.EXTRA_OPERATION, mode);
                 startActivityForResult(intent, REQUEST_COPY_DESTINATION);
             }
         }.execute(selected);
     }
 
-    private State getDisplayState() {
+    @Override
+    public void initDocumentHolder(DocumentHolder holder) {
+        holder.addClickListener(mItemClickListener);
+        holder.addOnKeyListener(mSelectionManager);
+    }
+
+    @Override
+    public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) {
+        if (DEBUG_ENABLE_DND) {
+            setupDragAndDropOnDocumentView(holder.itemView, cursor);
+        }
+    }
+
+    @Override
+    public State getDisplayState() {
         return ((BaseActivity) getActivity()).getDisplayState();
     }
 
+    @Override
+    public Model getModel() {
+        return mModel;
+    }
+
+    @Override
+    public boolean isDocumentEnabled(String docMimeType, int docFlags) {
+        return mTuner.isDocumentEnabled(docMimeType, docFlags);
+    }
+
     void showEmptyView() {
         mEmptyView.setVisibility(View.VISIBLE);
         mRecView.setVisibility(View.GONE);
@@ -920,240 +975,6 @@
         mRecView.setVisibility(View.VISIBLE);
     }
 
-    final class DocumentsAdapter
-            extends RecyclerView.Adapter<DocumentHolder>
-            implements Model.UpdateListener {
-
-        private static final String TAG = "DocumentsAdapter";
-        public static final int ITEM_TYPE_LAYOUT_DIVIDER = 0;
-        public static final int ITEM_TYPE_DOCUMENT = 1;
-        public static final int ITEM_TYPE_DIRECTORY = 2;
-
-        /**
-         * An ordered list of model IDs. This is the data structure that determines what shows up in
-         * the UI, and where.
-         */
-        private List<String> mModelIds = new ArrayList<>();
-
-        // The list is divided into two segments - directories, and everything else. Record the
-        // position where the transition happens.
-        private int mDividerPosition;
-
-        public GridLayoutManager.SpanSizeLookup createSpanSizeLookup() {
-            return new GridLayoutManager.SpanSizeLookup() {
-                @Override
-                public int getSpanSize(int position) {
-                    // Make layout whitespace span the grid. This has the effect of breaking
-                    // grid rows whenever layout whitespace is encountered.
-                    if (getItemViewType(position) == ITEM_TYPE_LAYOUT_DIVIDER) {
-                        return mColumnCount;
-                    } else {
-                        return 1;
-                    }
-                }
-            };
-        }
-
-        @Override
-        public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            if (viewType == ITEM_TYPE_LAYOUT_DIVIDER) {
-                return new EmptyDocumentHolder(getContext());
-            };
-
-            DocumentHolder holder = null;
-            final State state = getDisplayState();
-            switch (state.derivedMode) {
-                case MODE_GRID:
-                    switch (viewType) {
-                        case ITEM_TYPE_DIRECTORY:
-                            holder = new GridDirectoryHolder(getContext(), parent);
-                            break;
-                        case ITEM_TYPE_DOCUMENT:
-                            holder = new GridDocumentHolder(getContext(), parent, mIconHelper);
-                            break;
-                        default:
-                            throw new IllegalStateException("Unsupported layout type.");
-                    }
-                    break;
-                case MODE_LIST:
-                    holder = new ListDocumentHolder(getContext(), parent, mIconHelper);
-                    break;
-                case MODE_UNKNOWN:
-                default:
-                    throw new IllegalStateException("Unsupported layout mode.");
-            }
-
-            holder.addClickListener(mItemClickListener);
-            holder.addOnKeyListener(mSelectionManager);
-            return holder;
-        }
-
-        /**
-         * Deal with selection changed events by using a custom ItemAnimator that just changes the
-         * background color.  This works around focus issues (otherwise items lose focus when their
-         * selection state changes) but also optimizes change animations for selection.
-         */
-        @Override
-        public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) {
-            if (holder.getItemViewType() == ITEM_TYPE_LAYOUT_DIVIDER) {
-                // Whitespace items are hidden elements with no data to bind.
-                return;
-            }
-
-            final View itemView = holder.itemView;
-
-            if (payload.contains(MultiSelectManager.SELECTION_CHANGED_MARKER)) {
-                final boolean selected = isSelected(mModelIds.get(position));
-                itemView.setActivated(selected);
-                return;
-            } else {
-                onBindViewHolder(holder, position);
-            }
-        }
-
-        @Override
-        public void onBindViewHolder(DocumentHolder holder, int position) {
-            if (holder.getItemViewType() == ITEM_TYPE_LAYOUT_DIVIDER) {
-                // Whitespace items are hidden elements with no data to bind.
-                return;
-            }
-
-            String modelId = mModelIds.get(position);
-            Cursor cursor = mModel.getItem(modelId);
-            holder.bind(cursor, modelId, getDisplayState());
-
-            final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
-            final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
-
-            holder.setSelected(isSelected(modelId));
-            holder.setEnabled(mTuner.isDocumentEnabled(docMimeType, docFlags));
-            if (DEBUG_ENABLE_DND) {
-                setupDragAndDropOnDocumentView(holder.itemView, cursor);
-            }
-        }
-
-        @Override
-        public int getItemCount() {
-            return mModelIds.size();
-        }
-
-        @Override
-        public void onModelUpdate(Model model) {
-            mModelIds = Lists.newArrayList(model.getModelIds());
-            // Start the divider at the end. That way if the code below encounters no documents
-            // (i.e. in a directory containing only directories), the divider is placed at the end
-            // of the list, as expected.
-            mDividerPosition = mModelIds.size();
-
-            // Walk down the list of IDs till we encounter something that's not a directory, and
-            // insert a whitespace element - this introduces a visual break in the grid between
-            // folders and documents.
-            // TODO: This code makes assumptions about the model, namely, that it performs a
-            // bucketed sort where directories will always be ordered before other files.  CBB.
-            for (int i = 0; i < mModelIds.size(); ++i) {
-                final String mimeType = getCursorString(
-                        model.getItem(mModelIds.get(i)), Document.COLUMN_MIME_TYPE);
-                if (!Document.MIME_TYPE_DIR.equals(mimeType)) {
-                    mDividerPosition = i;
-                    break;
-                }
-            }
-
-            mModelIds.add(mDividerPosition, null);
-        }
-
-        @Override
-        public void onModelUpdateFailed(Exception e) {
-            if (DEBUG) Log.d(TAG, "onModelUpdateFailed called ");
-            mModelIds.clear();
-        }
-
-        /**
-         * @return The model ID of the item at the given adapter position.
-         */
-        public String getModelId(int adapterPosition) {
-            return mModelIds.get(adapterPosition);
-        }
-
-        /**
-         * Hides a set of items from the associated RecyclerView.
-         *
-         * @param ids The Model IDs of the items to hide.
-         * @return A SparseArray that maps the hidden IDs to their old positions. This can be used
-         *         to {@link #unhide} the items if necessary.
-         */
-        public SparseArray<String> hide(String... ids) {
-            Set<String> toHide = Sets.newHashSet(ids);
-
-            // Proceed backwards through the list of items, because each removal causes the
-            // positions of all subsequent items to change.
-            SparseArray<String> hiddenItems = new SparseArray<>();
-            for (int i = mModelIds.size() - 1; i >= 0; --i) {
-                String id = mModelIds.get(i);
-                if (toHide.contains(id)) {
-                    hiddenItems.put(i, mModelIds.remove(i));
-                    notifyItemRemoved(i);
-                }
-            }
-
-            return hiddenItems;
-        }
-
-        /**
-         * Unhides a set of previously hidden items.
-         *
-         * @param ids A sparse array of IDs from a previous call to {@link #hide}.
-         */
-        public void unhide(SparseArray<String> ids) {
-            // Proceed backwards through the list of items, because each addition causes the
-            // positions of all subsequent items to change.
-            for (int i = ids.size() - 1; i >= 0; --i) {
-                int pos = ids.keyAt(i);
-                String id = ids.get(pos);
-                mModelIds.add(pos, id);
-                notifyItemInserted(pos);
-            }
-        }
-
-        /**
-         * Returns a list of model IDs of items currently in the adapter. Excludes items that are
-         * currently hidden (see {@link #hide(String...)}).
-         *
-         * @return A list of Model IDs.
-         */
-        public List<String> getModelIds() {
-            return mModelIds;
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            if (position < mDividerPosition) {
-                return ITEM_TYPE_DIRECTORY;
-            } else if (position == mDividerPosition) {
-                return ITEM_TYPE_LAYOUT_DIVIDER;
-            } else {
-                return ITEM_TYPE_DOCUMENT;
-            }
-        }
-
-        /**
-         * Triggers item-change notifications by stable ID. Passing an unrecognized ID will result
-         * in a warning in logcat, but no other error.
-         *
-         * @param id
-         * @param selectionChangedMarker
-         */
-        public void notifyItemChanged(String id, String selectionChangedMarker) {
-            int position = mModelIds.indexOf(id);
-
-            if (position >= 0) {
-                notifyItemChanged(position, selectionChangedMarker);
-            } else {
-                Log.w(TAG, "Item change notification received for unknown item: " + id);
-            }
-        }
-    }
-
     private String findCommonMimeType(List<String> mimeTypes) {
         String[] commonType = mimeTypes.get(0).split("/");
         if (commonType.length != 2) {
@@ -1234,7 +1055,7 @@
             tmpStack = curStack;
         }
 
-        CopyService.start(getActivity(), docs, tmpStack, CopyService.TRANSFER_MODE_COPY);
+        FileOperations.copy(getActivity(), docs, tmpStack);
     }
 
     private ClipData getClipDataFromDocuments(List<DocumentInfo> docs) {
@@ -1504,7 +1325,8 @@
         abstract void onDocumentsReady(List<DocumentInfo> docs);
     }
 
-    boolean isSelected(String modelId) {
+    @Override
+    public boolean isSelected(String modelId) {
         return mSelectionManager.getSelection().contains(modelId);
     }
 
@@ -1520,7 +1342,7 @@
         }
     }
 
-    private class ModelUpdateListener implements Model.UpdateListener {
+    private final class ModelUpdateListener implements Model.UpdateListener {
         @Override
         public void onModelUpdate(Model model) {
             if (model.info != null || model.error != null) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java
new file mode 100644
index 0000000..43c2f63
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.DocumentsContract.Document;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.SparseArray;
+
+import com.android.documentsui.State;
+
+import java.nio.channels.UnsupportedAddressTypeException;
+import java.util.List;
+
+/**
+ * DocumentsAdapter provides glue between a directory Model, and RecylcerView. We've
+ * abstracted this a bit in order to decompose some specialized support
+ * for adding dummy layout objects (@see SectionBreakDocumentsAdapter). Handling of the
+ * dummy layout objects was error prone when interspersed with the core mode / adapter code.
+ *
+ * @see ModelBackedDocumentsAdapter
+ * @see SectionBreakDocumentsAdapter
+ */
+abstract class DocumentsAdapter
+        extends RecyclerView.Adapter<DocumentHolder>
+        implements Model.UpdateListener {
+
+    // Payloads for notifyItemChange to distinguish between selection and other events.
+    static final String SELECTION_CHANGED_MARKER = "Selection-Changed";
+
+    /**
+     * Returns a list of model IDs of items currently in the adapter. Excludes items that are
+     * currently hidden (see {@link #hide(String...)}).
+     *
+     * @return A list of Model IDs.
+     */
+    abstract List<String> getModelIds();
+
+    /**
+     * Triggers item-change notifications by stable ID (as opposed to position).
+     * Passing an unrecognized ID will result in a warning in logcat, but no other error.
+     */
+    abstract void onItemSelectionChanged(String id);
+
+    /**
+     * @return The model ID of the item at the given adapter position.
+     */
+    abstract String getModelId(int position);
+
+    /**
+     * Hides a set of items from the associated RecyclerView.
+     *
+     * @param ids The Model IDs of the items to hide.
+     * @return A SparseArray that maps the hidden IDs to their old positions. This can be used
+     *         to {@link #unhide} the items if necessary.
+     */
+    abstract public SparseArray<String> hide(String... ids);
+
+    /**
+     * Unhides a set of previously hidden items.
+     *
+     * @param ids A sparse array of IDs from a previous call to {@link #hide}.
+     */
+    abstract void unhide(SparseArray<String> ids);
+
+    /**
+     * Returns a class that yields the span size for a particular element. This is
+     * primarily useful in {@link SectionBreakDocumentsAdapterWrapper} where
+     * we adjust sizes.
+     */
+    GridLayoutManager.SpanSizeLookup createSpanSizeLookup() {
+        throw new UnsupportedAddressTypeException();
+    }
+
+    static boolean isDirectory(Cursor cursor) {
+        final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+        return Document.MIME_TYPE_DIR.equals(mimeType);
+    }
+
+    boolean isDirectory(Model model, int position) {
+        String modelId = getModelIds().get(position);
+        Cursor cursor = model.getItem(modelId);
+        return isDirectory(cursor);
+    }
+
+    /**
+     * Environmental access for View adapter implementations.
+     */
+    interface Environment {
+        Context getContext();
+        int getColumnCount();
+        State getDisplayState();
+        boolean isSelected(String id);
+        Model getModel();
+        boolean isDocumentEnabled(String mimeType, int flags);
+        void initDocumentHolder(DocumentHolder holder);
+        void onBindDocumentHolder(DocumentHolder holder, Cursor cursor);
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java
deleted file mode 100644
index ab67a5b..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 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.documentsui.dirlist;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.widget.Space;
-
-import com.android.documentsui.R;
-import com.android.documentsui.State;
-
-final class EmptyDocumentHolder extends DocumentHolder {
-    public EmptyDocumentHolder(Context context) {
-        super(context, new Space(context));
-
-        // Per UX spec, this puts a bigger gap between the folders and documents in the grid.
-        final int gridMargin = context.getResources().getDimensionPixelSize(R.dimen.grid_item_margin);
-        itemView.setMinimumHeight(gridMargin * 2);
-    }
-
-    public void bind(Cursor cursor, String modelId, State state) {
-        // Nothing to bind.
-        return;
-    }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDirectoryHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
index 11ff263..e672327 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDirectoryHolder.java
@@ -23,6 +23,7 @@
 import android.database.Cursor;
 import android.provider.DocumentsContract.Document;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.documentsui.R;
@@ -30,10 +31,24 @@
 
 final class GridDirectoryHolder extends DocumentHolder {
     final TextView mTitle;
+    private ImageView mIconCheck;
+    private ImageView mIconMime;
+
     public GridDirectoryHolder(Context context, ViewGroup parent) {
         super(context, parent, R.layout.item_dir_grid);
 
         mTitle = (TextView) itemView.findViewById(android.R.id.title);
+        mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime_sm);
+        mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
+    }
+
+    @Override
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        float checkAlpha = selected ? 1f : 0f;
+
+        mIconCheck.animate().alpha(checkAlpha).start();
+        mIconMime.animate().alpha(1f - checkAlpha).start();
     }
 
     /**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java
index 63c667b..c4ac0f5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java
@@ -32,6 +32,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.documentsui.IconUtils;
 import com.android.documentsui.R;
 import com.android.documentsui.RootCursorWrapper;
 import com.android.documentsui.Shared;
@@ -43,8 +44,10 @@
     final TextView mTitle;
     final TextView mDate;
     final TextView mSize;
-    final ImageView mIconMime;
+    final ImageView mIconMimeLg;
+    final ImageView mIconMimeSm;
     final ImageView mIconThumb;
+    final ImageView mIconCheck;
     final IconHelper mIconHelper;
 
     public GridDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
@@ -53,11 +56,23 @@
         mTitle = (TextView) itemView.findViewById(android.R.id.title);
         mDate = (TextView) itemView.findViewById(R.id.date);
         mSize = (TextView) itemView.findViewById(R.id.size);
-        mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
+        mIconMimeLg = (ImageView) itemView.findViewById(R.id.icon_mime_lg);
+        mIconMimeSm = (ImageView) itemView.findViewById(R.id.icon_mime_sm);
         mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
+        mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
+
         mIconHelper = iconHelper;
     }
 
+    @Override
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        float checkAlpha = selected ? 1f : 0f;
+
+        mIconCheck.animate().alpha(checkAlpha).start();
+        mIconMimeSm.animate().alpha(1f - checkAlpha).start();
+    }
+
     /**
      * Bind this view to the given document for display.
      * @param cursor Pointing to the item to be bound.
@@ -80,13 +95,15 @@
 
         mIconHelper.stopLoading(mIconThumb);
 
-        mIconMime.animate().cancel();
-        mIconMime.setAlpha(1f);
+        mIconMimeLg.animate().cancel();
+        mIconMimeLg.setAlpha(1f);
         mIconThumb.animate().cancel();
         mIconThumb.setAlpha(0f);
 
+        mIconMimeSm.setImageDrawable(IconUtils.loadMimeIcon(mContext, docMimeType));
+
         final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
-        mIconHelper.loadThumbnail(uri, docMimeType, docFlags, docIcon, mIconThumb, mIconMime);
+        mIconHelper.loadThumbnail(uri, docMimeType, docFlags, docIcon, mIconThumb, mIconMimeLg);
 
         if (mHideTitles) {
             mTitle.setVisibility(View.GONE);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java
index ff70eaf..0314077 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/IconHelper.java
@@ -91,7 +91,8 @@
                 thumbSize = mContext.getResources().getDimensionPixelSize(R.dimen.grid_width);
                 break;
             case MODE_LIST:
-                thumbSize = mContext.getResources().getDimensionPixelSize(R.dimen.icon_size);
+                thumbSize = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.list_item_thumbnail_size);
                 break;
             case MODE_UNKNOWN:
             default:
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
index c22e91d..00ea27b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
@@ -44,7 +44,7 @@
     final TextView mSize;
     final ImageView mIconMime;
     final ImageView mIconThumb;
-    final ImageView mIcon1;
+    final ImageView mIconCheck;
     final IconHelper mIconHelper;
 
     public ListDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
@@ -56,11 +56,21 @@
         mSize = (TextView) itemView.findViewById(R.id.size);
         mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
         mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
-        mIcon1 = (ImageView) itemView.findViewById(android.R.id.icon1);
+        mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
 
         mIconHelper = iconHelper;
     }
 
+    @Override
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        float checkAlpha = selected ? 1f : 0f;
+
+        mIconCheck.animate().alpha(checkAlpha).start();
+        mIconMime.animate().alpha(1f - checkAlpha).start();
+        mIconThumb.animate().alpha(1f - checkAlpha).start();
+    }
+
     /**
      * Bind this view to the given document for display.
      * @param cursor Pointing to the item to be bound.
@@ -86,7 +96,9 @@
         mIconHelper.stopLoading(mIconThumb);
 
         mIconMime.animate().cancel();
+        mIconMime.setAlpha(1f);
         mIconThumb.animate().cancel();
+        mIconThumb.setAlpha(0f);
 
         final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
         mIconHelper.loadThumbnail(uri, docMimeType, docFlags, docIcon, mIconThumb, mIconMime);
@@ -121,6 +133,5 @@
         final float iconAlpha = enabled ? 1f : 0.5f;
         mIconMime.setAlpha(iconAlpha);
         mIconThumb.setAlpha(iconAlpha);
-        mIcon1.setAlpha(iconAlpha);
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
index bea38c6..cf21d15 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
@@ -35,7 +35,6 @@
 import android.provider.DocumentsContract.Document;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
-import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 
 import com.android.documentsui.BaseActivity.SiblingProvider;
@@ -74,7 +73,7 @@
     @Nullable String info;
     @Nullable String error;
 
-    Model(Context context, RecyclerView.Adapter<?> viewAdapter) {
+    Model(Context context) {
         mContext = context;
     }
 
@@ -87,8 +86,19 @@
     private static String createModelId(Cursor c) {
         // TODO: Maybe more efficient to use just the document ID, in cases where there is only one
         // authority (which should be the majority of cases).
-        return getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY) +
-                "|" + getCursorString(c, Document.COLUMN_DOCUMENT_ID);
+        return createModelId(
+                getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY),
+                getCursorString(c, Document.COLUMN_DOCUMENT_ID));
+    }
+
+    /**
+     * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique
+     * string that can be used to identify the document referred to by the cursor.
+     *
+     * @param c A cursor that refers to a document.
+     */
+    static String createModelId(String authority, String docId) {
+        return authority + "|" + docId;
     }
 
     private void notifyUpdateListeners() {
@@ -177,7 +187,7 @@
                     }
                     break;
                 case SORT_ORDER_LAST_MODIFIED:
-                    longValues[pos] = getCursorLong(mCursor, Document.COLUMN_LAST_MODIFIED);
+                    longValues[pos] = getLastModified(mCursor);
                     stringValues[pos] = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
                     break;
                 case SORT_ORDER_SIZE:
@@ -299,11 +309,19 @@
                 } else {
                     final long lhs = pivotValue;
                     final long rhs = sortKey[mid];
-                    // Sort in descending numerical order. This matches legacy behaviour, which yields
-                    // largest or most recent items on top.
+                    // Sort in descending numerical order. This matches legacy behaviour, which
+                    // yields largest or most recent items on top.
                     compare = -Long.compare(lhs, rhs);
                 }
 
+                // If numerical comparison yields a tie, use document ID as a tie breaker.  This
+                // will yield stable results even if incoming items are continually shuffling and
+                // have identical numerical sort keys.  One common example of this scenario is seen
+                // when sorting a set of active downloads by mod time.
+                if (compare == 0) {
+                    compare = pivotId.compareTo(ids.get(mid));
+                }
+
                 if (compare < 0) {
                     right = mid;
                 } else {
@@ -340,6 +358,16 @@
         }
     }
 
+    /**
+     * @return Timestamp for the given document. Some docs (e.g. active downloads) have a null
+     * timestamp - these will be replaced with MAX_LONG so that such files get sorted to the top
+     * when sorting by date.
+     */
+    long getLastModified(Cursor cursor) {
+        long l = getCursorLong(mCursor, Document.COLUMN_LAST_MODIFIED);
+        return (l == -1) ? Long.MAX_VALUE : l;
+    }
+
     @Nullable Cursor getItem(String modelId) {
         Integer pos = mPositions.get(modelId);
         if (pos != null) {
@@ -404,8 +432,14 @@
 
         @Override
         protected Void doInBackground(Selection... selected) {
-            List<DocumentInfo> toDelete = getDocuments(selected[0]);
-            mHadTrouble = false;
+            List<DocumentInfo> toDelete = null;
+            try {
+                toDelete = getDocuments(selected[0]);
+            } catch (NullPointerException e) {
+                Log.w(TAG, "Failed to retrieve documents for delete.");
+                mHadTrouble = true;
+                return null;
+            }
 
             for (DocumentInfo doc : toDelete) {
                 if (!doc.isDeleteSupported()) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
new file mode 100644
index 0000000..68756a3
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.State.MODE_GRID;
+import static com.android.documentsui.State.MODE_LIST;
+import static com.android.documentsui.State.MODE_UNKNOWN;
+import static com.android.documentsui.model.DocumentInfo.getCursorInt;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
+import android.database.Cursor;
+import android.provider.DocumentsContract.Document;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.ViewGroup;
+
+import com.android.documentsui.State;
+
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Adapts from dirlist.Model to something RecyclerView understands.
+ */
+final class ModelBackedDocumentsAdapter extends DocumentsAdapter {
+
+    private static final String TAG = "ModelBackedDocumentsAdapter";
+    public static final int ITEM_TYPE_DOCUMENT = 1;
+    public static final int ITEM_TYPE_DIRECTORY = 2;
+
+    // Provides access to information needed when creating and view holders. This
+    // isn't an ideal pattern (more transitive dependency stuff) but good enough for now.
+    private final Environment mEnv;
+    private final IconHelper mIconHelper;  // a transitive dependency of the holders.
+
+    /**
+     * An ordered list of model IDs. This is the data structure that determines what shows up in
+     * the UI, and where.
+     */
+    private List<String> mModelIds = new ArrayList<>();
+
+    // List of files that have been deleted. Some transient directory updates
+    // may happen while files are being deleted. During this time we don't
+    // want once-hidden files to be re-shown. We only remove
+    // items from this list when we get a model update where the model
+    // does not contain a corresponding id. This ensures hidden entries
+    // don't momentarily re-appear if we get intermediate updates from
+    // the file system.
+    private Set<String> mHiddenIds = new HashSet<>();
+
+    public ModelBackedDocumentsAdapter(Environment env, IconHelper iconHelper) {
+        mEnv = env;
+        mIconHelper = iconHelper;
+    }
+
+    @Override
+    public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        DocumentHolder holder = null;
+        final State state = mEnv.getDisplayState();
+        switch (state.derivedMode) {
+            case MODE_GRID:
+                switch (viewType) {
+                    case ITEM_TYPE_DIRECTORY:
+                        holder = new GridDirectoryHolder(mEnv.getContext(), parent);
+                        break;
+                    case ITEM_TYPE_DOCUMENT:
+                        holder = new GridDocumentHolder(mEnv.getContext(), parent, mIconHelper);
+                        break;
+                    default:
+                        throw new IllegalStateException("Unsupported layout type.");
+                }
+                break;
+            case MODE_LIST:
+                holder = new ListDocumentHolder(mEnv.getContext(), parent, mIconHelper);
+                break;
+            case MODE_UNKNOWN:
+            default:
+                throw new IllegalStateException("Unsupported layout mode.");
+        }
+
+        mEnv.initDocumentHolder(holder);
+        return holder;
+    }
+
+    @Override
+    public void onBindViewHolder(DocumentHolder holder, int position, List<Object> payload) {
+        if (payload.contains(SELECTION_CHANGED_MARKER)) {
+            final boolean selected = mEnv.isSelected(mModelIds.get(position));
+            holder.setSelected(selected);
+        } else {
+            onBindViewHolder(holder, position);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(DocumentHolder holder, int position) {
+        String modelId = mModelIds.get(position);
+        Cursor cursor = mEnv.getModel().getItem(modelId);
+        holder.bind(cursor, modelId, mEnv.getDisplayState());
+
+        final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+        final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+
+        holder.setSelected(mEnv.isSelected(modelId));
+        holder.setEnabled(mEnv.isDocumentEnabled(docMimeType, docFlags));
+
+        mEnv.onBindDocumentHolder(holder, cursor);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mModelIds.size();
+    }
+
+    @Override
+    public void onModelUpdate(Model model) {
+        if (DEBUG && mHiddenIds.size() > 0) {
+            Log.d(TAG, "Updating model with hidden ids: " + mHiddenIds);
+        }
+
+        List<String> modelIds = model.getModelIds();
+        mModelIds = new ArrayList<>(modelIds.size());
+        for (String id : modelIds) {
+            if (!mHiddenIds.contains(id)) {
+                mModelIds.add(id);
+            } else {
+                if (DEBUG) Log.d(TAG, "Omitting hidden id from model during update: " + id);
+            }
+        }
+
+        // Finally remove any hidden ids that aren't present in the model.
+        // This assumes that model updates represent a complete set of files.
+        mHiddenIds.retainAll(mModelIds);
+    }
+
+    @Override
+    public void onModelUpdateFailed(Exception e) {
+        Log.w(TAG, "Model update failed.", e);
+        mModelIds.clear();
+    }
+
+    @Override
+    public String getModelId(int adapterPosition) {
+        return mModelIds.get(adapterPosition);
+    }
+
+    @Override
+    public SparseArray<String> hide(String... ids) {
+        if (DEBUG) Log.d(TAG, "Hiding ids: " + ids);
+        Set<String> toHide = Sets.newHashSet(ids);
+
+        // Proceed backwards through the list of items, because each removal causes the
+        // positions of all subsequent items to change.
+        SparseArray<String> hiddenItems = new SparseArray<>();
+        for (int i = mModelIds.size() - 1; i >= 0; --i) {
+            String id = mModelIds.get(i);
+            if (toHide.contains(id)) {
+                mHiddenIds.add(id);
+                hiddenItems.put(i, mModelIds.remove(i));
+                notifyItemRemoved(i);
+            }
+        }
+
+        return hiddenItems;
+    }
+
+    @Override
+    public void unhide(SparseArray<String> ids) {
+        if (DEBUG) Log.d(TAG, "Un-iding ids: " + ids);
+
+        // An ArrayList can shrink at runtime...and in fact
+        // it does when we clear it completely.
+        // This means we can't call add(pos, id) without
+        // first checking the list size.
+        List<String> oldIds = mModelIds;
+        mModelIds = new ArrayList<>(oldIds.size() + ids.size());
+        mModelIds.addAll(oldIds);
+
+        // Finally insert the unhidden items.
+        for (int i = 0; i < ids.size(); i++) {
+            int pos = ids.keyAt(i);
+            String id = ids.get(pos);
+            mHiddenIds.remove(id);
+            mModelIds.add(pos, id);
+            notifyItemInserted(pos);
+        }
+    }
+
+    @Override
+    public List<String> getModelIds() {
+        return mModelIds;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return isDirectory(mEnv.getModel(), position)
+                ? ITEM_TYPE_DIRECTORY
+                : ITEM_TYPE_DOCUMENT;
+    }
+
+    @Override
+    public void onItemSelectionChanged(String id) {
+        int position = mModelIds.indexOf(id);
+
+        if (position >= 0) {
+            notifyItemChanged(position, SELECTION_CHANGED_MARKER);
+        } else {
+            Log.w(TAG, "Item change notification received for unknown item: " + id);
+        }
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index e47af67..d868fb4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -68,24 +68,23 @@
 
     private final Selection mSelection = new Selection();
 
-    private Range mRanger;
-    private SelectionEnvironment mEnvironment;
-
+    private final SelectionEnvironment mEnvironment;
+    private final DocumentsAdapter mAdapter;
     private final List<MultiSelectManager.Callback> mCallbacks = new ArrayList<>(1);
 
+    private Range mRanger;
     private boolean mSingleSelect;
 
-    // Payloads for notifyItemChange to distinguish between selection and other events.
-    public static final String SELECTION_CHANGED_MARKER = "Selection-Changed";
-
     @Nullable private BandController mBandManager;
 
+
     /**
      * @param recyclerView
      * @param mode Selection mode
      */
-    public MultiSelectManager(final RecyclerView recyclerView, int mode) {
-        this(new RuntimeSelectionEnvironment(recyclerView), mode);
+    public MultiSelectManager(
+            final RecyclerView recyclerView, DocumentsAdapter adapter, int mode) {
+        this(new RuntimeSelectionEnvironment(recyclerView), adapter, mode);
 
         if (mode == MODE_MULTIPLE) {
             mBandManager = new BandController();
@@ -136,11 +135,12 @@
      * @hide
      */
     @VisibleForTesting
-    MultiSelectManager(SelectionEnvironment environment, int mode) {
+    MultiSelectManager(SelectionEnvironment environment, DocumentsAdapter adapter, int mode) {
         mEnvironment = checkNotNull(environment, "'environment' cannot be null.");
+        mAdapter = checkNotNull(adapter, "'adapter' cannot be null.");
         mSingleSelect = mode == MODE_SINGLE;
 
-        mEnvironment.registerDataObserver(
+        mAdapter.registerAdapterDataObserver(
                 new RecyclerView.AdapterDataObserver() {
 
                     private List<String> mModelIds;
@@ -149,7 +149,7 @@
                     public void onChanged() {
                         // TODO: This is causing b/22765812
                         mSelection.clear();
-                        mModelIds = mEnvironment.getModelIds();
+                        mModelIds = mAdapter.getModelIds();
                     }
 
                     @Override
@@ -339,7 +339,10 @@
             if (DEBUG) Log.d(TAG, "Ignoring toggle for element with no position.");
             return;
         }
-        toggleSelection(mEnvironment.getModelIdFromAdapterPosition(position));
+        String id = mAdapter.getModelId(position);
+        if (id != null) {
+            toggleSelection(id);
+        }
     }
 
     /**
@@ -348,6 +351,7 @@
      * @param modelId
      */
     public void toggleSelection(String modelId) {
+        checkNotNull(modelId);
         boolean changed = false;
         if (mSelection.contains(modelId)) {
             changed = attemptDeselect(modelId);
@@ -387,7 +391,7 @@
             return;
         }
 
-        if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(position))) {
+        if (mSelection.contains(mAdapter.getModelId(position))) {
             mRanger = new Range(position);
         }
     }
@@ -404,7 +408,11 @@
     private void updateRange(int begin, int end, boolean selected) {
         checkState(end >= begin);
         for (int i = begin; i <= end; i++) {
-            String id = mEnvironment.getModelIdFromAdapterPosition(i);
+            String id = mAdapter.getModelId(i);
+            if (id == null) {
+                continue;
+            }
+
             if (selected) {
                 boolean canSelect = notifyBeforeItemStateChange(id, true);
                 if (canSelect) {
@@ -436,6 +444,7 @@
      * @return True if the update was applied.
      */
     private boolean attemptDeselect(String id) {
+        checkArgument(id != null);
         if (notifyBeforeItemStateChange(id, false)) {
             mSelection.remove(id);
             notifyItemStateChanged(id, false);
@@ -462,11 +471,12 @@
      * (identified by {@code position}) changes.
      */
     private void notifyItemStateChanged(String id, boolean selected) {
+        checkArgument(id != null);
         int lastListener = mCallbacks.size() - 1;
         for (int i = lastListener; i > -1; i--) {
             mCallbacks.get(i).onItemStateChanged(id, selected);
         }
-        mEnvironment.notifyItemChanged(id);
+        mAdapter.onItemSelectionChanged(id);
     }
 
     /**
@@ -613,7 +623,7 @@
          * @param id
          * @return true if the position is currently selected.
          */
-        public boolean contains(String id) {
+        public boolean contains(@Nullable String id) {
             return mTotalSelection.contains(id);
         }
 
@@ -804,11 +814,6 @@
         int getChildCount();
         int getVisibleChildCount();
         void focusItem(int position);
-        String getModelIdFromAdapterPosition(int position);
-        int getItemCount();
-        List<String> getModelIds();
-        void notifyItemChanged(String id);
-        void registerDataObserver(RecyclerView.AdapterDataObserver observer);
     }
 
     /** Recycler view facade implementation backed by good ol' RecyclerView. */
@@ -818,11 +823,9 @@
         private final Drawable mBand;
 
         private boolean mIsOverlayShown = false;
-        private DirectoryFragment.DocumentsAdapter mAdapter;
 
-        RuntimeSelectionEnvironment(RecyclerView rv) {
-            mView = rv;
-            mAdapter = (DirectoryFragment.DocumentsAdapter) rv.getAdapter();
+        RuntimeSelectionEnvironment(RecyclerView view) {
+            mView = view;
             mBand = mView.getContext().getTheme().getDrawable(R.drawable.band_select_overlay);
         }
 
@@ -841,11 +844,6 @@
         }
 
         @Override
-        public String getModelIdFromAdapterPosition(int position) {
-            return mAdapter.getModelId(position);
-        }
-
-        @Override
         public void addOnScrollListener(RecyclerView.OnScrollListener listener) {
             mView.addOnScrollListener(listener);
         }
@@ -947,40 +945,31 @@
             if (vh != null) {
                 vh.itemView.requestFocus();
             } else {
-                // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll
-                // handling logic below more complicated.  See b/24865658.
-                mView.scrollToPosition(pos);
+                mView.smoothScrollToPosition(pos);
                 // Set a one-time listener to request focus when the scroll has completed.
                 mView.addOnScrollListener(
                     new RecyclerView.OnScrollListener() {
                         @Override
-                        public void onScrolled(RecyclerView view, int dx, int dy) {
-                            view.findViewHolderForAdapterPosition(pos).itemView.requestFocus();
-                            view.removeOnScrollListener(this);
+                        public void onScrollStateChanged (RecyclerView view, int newState) {
+                            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+                                // When scrolling stops, find the item and focus it.
+                                RecyclerView.ViewHolder vh =
+                                        view.findViewHolderForAdapterPosition(pos);
+                                if (vh != null) {
+                                    vh.itemView.requestFocus();
+                                } else {
+                                    // This might happen in weird corner cases, e.g. if the user is
+                                    // scrolling while a delete operation is in progress.  In that
+                                    // case, just don't attempt to focus the missing item.
+                                    Log.w(
+                                        TAG, "Unable to focus position " + pos + " after a scroll");
+                                }
+                                view.removeOnScrollListener(this);
+                            }
                         }
                     });
             }
         }
-
-        @Override
-        public void notifyItemChanged(String id) {
-            mAdapter.notifyItemChanged(id, SELECTION_CHANGED_MARKER);
-        }
-
-        @Override
-        public int getItemCount() {
-            return mAdapter.getItemCount();
-        }
-
-        @Override
-        public void registerDataObserver(RecyclerView.AdapterDataObserver observer) {
-            mAdapter.registerAdapterDataObserver(observer);
-        }
-
-        @Override
-        public List<String> getModelIds() {
-            return mAdapter.getModelIds();
-        }
     }
 
     public interface Callback {
@@ -1037,7 +1026,7 @@
             mModelBuilder = new Runnable() {
                 @Override
                 public void run() {
-                    mModel = new GridModel(mEnvironment);
+                    mModel = new GridModel(mEnvironment, mAdapter);
                     mModel.addOnSelectionChangedListener(BandController.this);
                 }
             };
@@ -1088,7 +1077,7 @@
             return !isActive()
                     && e.isMouseEvent()  // a mouse
                     && e.isActionDown()  // the initial button press
-                    && mEnvironment.getItemCount() > 0
+                    && mAdapter.getItemCount() > 0
                     && e.getItemPosition() == RecyclerView.NO_ID;  // in empty space
         }
 
@@ -1165,7 +1154,7 @@
             mModel.endSelection();
             int firstSelected = mModel.getPositionNearestOrigin();
             if (firstSelected != NOT_SET) {
-                if (mSelection.contains(mEnvironment.getModelIdFromAdapterPosition(firstSelected))) {
+                if (mSelection.contains(mAdapter.getModelId(firstSelected))) {
                     // TODO: firstSelected should really be lastSelected, we want to anchor the item
                     // where the mouse-up occurred.
                     setSelectionRangeBegin(firstSelected);
@@ -1327,6 +1316,8 @@
         private static final int LOWER_RIGHT = LOWER | RIGHT;
 
         private final SelectionEnvironment mHelper;
+        private final DocumentsAdapter mAdapter;
+
         private final List<OnSelectionChangedListener> mOnSelectionChangedListeners =
                 new ArrayList<>();
 
@@ -1364,8 +1355,9 @@
         // should expand from when Shift+click is used.
         private int mPositionNearestOrigin = NOT_SET;
 
-        GridModel(SelectionEnvironment helper) {
+        GridModel(SelectionEnvironment helper, DocumentsAdapter adapter) {
             mHelper = helper;
+            mAdapter = adapter;
             mHelper.addOnScrollListener(this);
         }
 
@@ -1568,7 +1560,7 @@
                     // position. Use a sentry value to prevent erroneously selecting item 0.
                     int position = items.get(items.keyAt(row), NOT_SET);
                     if (position != NOT_SET) {
-                        String id = mHelper.getModelIdFromAdapterPosition(position);
+                        String id = mAdapter.getModelId(position);
                         if (id != null) {
                             // The adapter inserts items for UI layout purposes that aren't associated
                             // with files.  Those will have a null model ID.  Don't select them.
@@ -1932,37 +1924,41 @@
         // Here we unpack information from the event and pass it to an more
         // easily tested method....basically eliminating the need to synthesize
         // events and views and so on in our tests.
-        int position = findTargetPosition(view, keyCode);
-        if (position == RecyclerView.NO_POSITION) {
+        int endPos = findTargetPosition(view, keyCode);
+        if (endPos == RecyclerView.NO_POSITION) {
             // If there is no valid navigation target, don't handle the keypress.
             return false;
         }
 
-        return attemptChangeFocus(position, event.isShiftPressed());
+        int startPos = mEnvironment.getAdapterPositionForChildView(view);
+
+        return changeFocus(startPos, endPos, event.isShiftPressed());
     }
 
     /**
+     * @param startPosition The current focus position.
      * @param targetPosition The adapter position to focus.
      * @param extendSelection
      */
     @VisibleForTesting
-    boolean attemptChangeFocus(int targetPosition, boolean extendSelection) {
+    boolean changeFocus(int startPosition, int targetPosition, boolean extendSelection) {
         // Focus the new file.
         mEnvironment.focusItem(targetPosition);
 
         if (extendSelection) {
-            if (!hasSelection()) {
-                // If there is no selection, start a selection when the user presses shift-arrow.
-                toggleSelection(targetPosition);
-                setSelectionRangeBegin(targetPosition);
-            } else if (!mSingleSelect) {
-                mRanger.snapSelection(targetPosition);
-                notifySelectionChanged();
-            } else {
+            if (mSingleSelect) {
                 // We're in single select and have an existing selection.
                 // Our best guess as to what the user would expect is to advance the selection.
                 clearSelection();
                 toggleSelection(targetPosition);
+            } else {
+                if (!hasSelection()) {
+                    // No selection - start a selection when the user presses shift-arrow.
+                    toggleSelection(startPosition);
+                    setSelectionRangeBegin(startPosition);
+                }
+                mRanger.snapSelection(targetPosition);
+                notifySelectionChanged();
             }
         }
 
@@ -1977,7 +1973,7 @@
         if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
             position = 0;
         } else if (keyCode == KeyEvent.KEYCODE_MOVE_END) {
-            position = mEnvironment.getItemCount() - 1;
+            position = mAdapter.getItemCount() - 1;
         } else {
             // Find a navigation target based on the arrow key that the user pressed.  Ignore
             // navigation targets that aren't items in the recycler view.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
new file mode 100644
index 0000000..2485ad9
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView.AdapterDataObserver;
+import android.util.SparseArray;
+import android.view.ViewGroup;
+import android.widget.Space;
+
+import com.android.documentsui.R;
+import com.android.documentsui.State;
+
+import java.util.List;
+
+/**
+ * Adapter wrapper that inserts a sort of line break item between directories and regular files.
+ * Only needs to be used in GRID mode...at this time.
+ */
+final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {
+
+    private static final String TAG = "SectionBreakDocumentsAdapterWrapper";
+    private static final int ITEM_TYPE_SECTION_BREAK = Integer.MAX_VALUE;
+
+    private final Environment mEnv;
+    private final DocumentsAdapter mDelegate;
+
+    private int mBreakPosition = -1;
+
+    SectionBreakDocumentsAdapterWrapper(Environment environment, DocumentsAdapter delegate) {
+        mEnv = environment;
+        mDelegate = delegate;
+
+        // Relay events published by our delegate to our listeners (presumably RecyclerView)
+        // with adjusted positions.
+        mDelegate.registerAdapterDataObserver(new EventRelay());
+    }
+
+    public GridLayoutManager.SpanSizeLookup createSpanSizeLookup() {
+        return new GridLayoutManager.SpanSizeLookup() {
+            @Override
+            public int getSpanSize(int position) {
+                // Make layout whitespace span the grid. This has the effect of breaking
+                // grid rows whenever layout whitespace is encountered.
+                if (getItemViewType(position) == ITEM_TYPE_SECTION_BREAK) {
+                    return mEnv.getColumnCount();
+                } else {
+                    return 1;
+                }
+            }
+        };
+    }
+
+    @Override
+    public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        if (viewType == ITEM_TYPE_SECTION_BREAK) {
+            return new EmptyDocumentHolder(mEnv.getContext());
+        } else {
+            return mDelegate.createViewHolder(parent, viewType);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(DocumentHolder holder, int p, List<Object> payload) {
+        if (holder.getItemViewType() != ITEM_TYPE_SECTION_BREAK) {
+            mDelegate.onBindViewHolder(holder, toDelegatePosition(p), payload);
+        } else {
+            ((EmptyDocumentHolder)holder).bind(mEnv.getDisplayState());
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(DocumentHolder holder, int p) {
+        if (holder.getItemViewType() != ITEM_TYPE_SECTION_BREAK) {
+            mDelegate.onBindViewHolder(holder, toDelegatePosition(p));
+        } else {
+            ((EmptyDocumentHolder)holder).bind(mEnv.getDisplayState());
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return mBreakPosition == -1
+                ? mDelegate.getItemCount()
+                : mDelegate.getItemCount() + 1;
+    }
+
+    @Override
+    public void onModelUpdate(Model model) {
+        mDelegate.onModelUpdate(model);
+        mBreakPosition = -1;
+
+        // Walk down the list of IDs till we encounter something that's not a directory, and
+        // insert a whitespace element - this introduces a visual break in the grid between
+        // folders and documents.
+        // TODO: This code makes assumptions about the model, namely, that it performs a
+        // bucketed sort where directories will always be ordered before other files. CBB.
+        List<String> modelIds = mDelegate.getModelIds();
+        for (int i = 0; i < modelIds.size(); i++) {
+            if (!isDirectory(model, i)) {
+                // If the break is the first thing in the list, then there are actually no
+                // directories. In that case, don't insert a break at all.
+                if (i > 0) {
+                    mBreakPosition = i;
+                }
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void onModelUpdateFailed(Exception e) {
+        mDelegate.onModelUpdateFailed(e);
+    }
+
+    @Override
+    public int getItemViewType(int p) {
+        if (p == mBreakPosition) {
+            return ITEM_TYPE_SECTION_BREAK;
+        } else {
+            return mDelegate.getItemViewType(toDelegatePosition(p));
+        }
+    }
+
+    /**
+     * Returns the position of an item in the delegate, adjusting
+     * values that are greater than the break position.
+     *
+     * @param p Position within the view
+     * @return Position within the delegate
+     */
+    private int toDelegatePosition(int p) {
+        return (mBreakPosition != -1 && p > mBreakPosition) ? p - 1 : p;
+    }
+
+    /**
+     * Returns the position of an item in the view, adjusting
+     * values that are greater than the break position.
+     *
+     * @param p Position within the delegate
+     * @return Position within the view
+     */
+    private int toViewPosition(int p) {
+        // If position is greater than or equal to the break, increase by one.
+        return (mBreakPosition != -1 && p >= mBreakPosition) ? p + 1 : p;
+    }
+
+    @Override
+    public SparseArray<String> hide(String... ids) {
+        // NOTE: We hear about these changes and adjust break position
+        // in our AdapterDataObserver.
+        return mDelegate.hide(ids);
+    }
+
+    @Override
+    void unhide(SparseArray<String> ids) {
+        // NOTE: We hear about these changes and adjust break position
+        // in our AdapterDataObserver.
+        mDelegate.unhide(ids);
+    }
+
+    @Override
+    List<String> getModelIds() {
+        return mDelegate.getModelIds();
+    }
+
+    @Override
+    String getModelId(int p) {
+        return (p == mBreakPosition) ? null : mDelegate.getModelId(toDelegatePosition(p));
+    }
+
+    @Override
+    public void onItemSelectionChanged(String id) {
+        mDelegate.onItemSelectionChanged(id);
+    }
+
+    // Listener we add to our delegate. This allows us to relay events published
+    // by the delegate to our listeners (presumably RecyclerView) with adjusted positions.
+    private final class EventRelay extends AdapterDataObserver {
+        public void onChanged() {
+            throw new UnsupportedOperationException();
+        }
+
+        public void onItemRangeChanged(int positionStart, int itemCount) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
+            checkArgument(itemCount == 1);
+            notifyItemRangeChanged(toViewPosition(positionStart), itemCount, payload);
+        }
+
+        public void onItemRangeInserted(int positionStart, int itemCount) {
+            checkArgument(itemCount == 1);
+            if (positionStart < mBreakPosition) {
+                mBreakPosition++;
+            }
+            notifyItemRangeInserted(toViewPosition(positionStart), itemCount);
+        }
+
+        public void onItemRangeRemoved(int positionStart, int itemCount) {
+            checkArgument(itemCount == 1);
+            if (positionStart < mBreakPosition) {
+                mBreakPosition--;
+            }
+            notifyItemRangeRemoved(toViewPosition(positionStart), itemCount);
+        }
+
+        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * The most elegant transparent blank box that spans N rows ever conceived.
+     */
+    private static final class EmptyDocumentHolder extends DocumentHolder {
+        final int mVisibleHeight;
+
+        public EmptyDocumentHolder(Context context) {
+            super(context, new Space(context));
+
+            // Per UX spec, this puts a bigger gap between the folders and documents in the grid.
+            mVisibleHeight = context.getResources().getDimensionPixelSize(
+                    R.dimen.grid_item_margin);
+        }
+
+        public void bind(State state) {
+            bind(null, null, state);
+        }
+
+        @Override
+        public void bind(Cursor cursor, String modelId, State state) {
+            if (state.derivedMode == State.MODE_GRID) {
+                itemView.setMinimumHeight(mVisibleHeight);
+            } else {
+                itemView.setMinimumHeight(0);
+            }
+            return;
+        }
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 215c6e6..83df18c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -255,10 +255,6 @@
         return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
     }
 
-    public boolean isTypedDocument() {
-        return (flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) != 0;
-    }
-
     public int hashCode() {
         return derivedUri.hashCode() + mimeType.hashCode();
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 4caa891..12c0b8f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -174,7 +174,7 @@
             derivedIcon = R.drawable.ic_root_home;
             derivedType = TYPE_LOCAL;
         } else if (isExternalStorage()) {
-            derivedIcon = R.drawable.ic_root_sdcard;
+            derivedIcon = R.drawable.ic_root_smartphone;
             derivedType = TYPE_LOCAL;
         } else if (isDownloads()) {
             derivedIcon = R.drawable.ic_root_download;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
new file mode 100644
index 0000000..8f89b4e
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -0,0 +1,505 @@
+/*
+ * 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.documentsui.services;
+
+import static android.os.SystemClock.elapsedRealtime;
+import static com.android.documentsui.DocumentsApplication.acquireUnstableProviderOrThrow;
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.model.DocumentInfo.getCursorLong;
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import android.annotation.StringRes;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.android.documentsui.R;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+
+import libcore.io.IoUtils;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.List;
+
+class CopyJob extends Job {
+    private static final String TAG = "CopyJob";
+    private static final int PROGRESS_INTERVAL_MILLIS = 1000;
+    final List<DocumentInfo> mSrcFiles;
+
+    // Provider clients are acquired for the duration of each copy job. Note that there is an
+    // implicit assumption that all srcs come from the same authority.
+    ContentProviderClient srcClient;
+    ContentProviderClient dstClient;
+
+    private long mStartTime = -1;
+    private long mBatchSize;
+    private long mBytesCopied;
+    private long mLastNotificationTime;
+    // Speed estimation
+    private long mBytesCopiedSample;
+    private long mSampleTime;
+    private long mSpeed;
+    private long mRemainingTime;
+
+    /**
+     * Copies files to a destination identified by {@code destination}.
+     * @see @link {@link Job} constructor for most param descriptions.
+     *
+     * @param srcs List of files to be copied.
+     */
+    CopyJob(Context serviceContext, Context appContext, Listener listener,
+            String id, DocumentStack destination, List<DocumentInfo> srcs) {
+        super(OPERATION_COPY, serviceContext, appContext, listener, id, destination);
+
+        checkArgument(!srcs.isEmpty());
+        this.mSrcFiles = srcs;
+    }
+
+    @Override
+    Builder createProgressBuilder() {
+        return super.createProgressBuilder(
+                serviceContext.getString(R.string.copy_notification_title),
+                R.drawable.ic_menu_copy,
+                serviceContext.getString(android.R.string.cancel),
+                R.drawable.ic_cab_cancel);
+    }
+
+    @Override
+    public Notification getSetupNotification() {
+        return getSetupNotification(serviceContext.getString(R.string.copy_preparing));
+    }
+
+    public boolean shouldUpdateProgress() {
+        // Wait a while between updates :)
+        return elapsedRealtime() - mLastNotificationTime > PROGRESS_INTERVAL_MILLIS;
+    }
+
+    Notification getProgressNotification(@StringRes int msgId) {
+        double completed = (double) this.mBytesCopied / mBatchSize;
+        mProgressBuilder.setProgress(100, (int) (completed * 100), false);
+        mProgressBuilder.setContentInfo(
+                NumberFormat.getPercentInstance().format(completed));
+        if (mRemainingTime > 0) {
+            mProgressBuilder.setContentText(serviceContext.getString(msgId,
+                    DateUtils.formatDuration(mRemainingTime)));
+        } else {
+            mProgressBuilder.setContentText(null);
+        }
+
+        // Remember when we last returned progress so we can provide an answer
+        // in shouldUpdateProgress.
+        mLastNotificationTime = elapsedRealtime();
+        return mProgressBuilder.build();
+    }
+
+    public Notification getProgressNotification() {
+        return getProgressNotification(R.string.copy_remaining);
+    }
+
+    void onBytesCopied(long numBytes) {
+        this.mBytesCopied += numBytes;
+    }
+
+    /**
+     * Generates an estimate of the remaining time in the copy.
+     */
+    void updateRemainingTimeEstimate() {
+        long elapsedTime = elapsedRealtime() - mStartTime;
+
+        final long sampleDuration = elapsedTime - mSampleTime;
+        final long sampleSpeed = ((mBytesCopied - mBytesCopiedSample) * 1000) / sampleDuration;
+        if (mSpeed == 0) {
+            mSpeed = sampleSpeed;
+        } else {
+            mSpeed = ((3 * mSpeed) + sampleSpeed) / 4;
+        }
+
+        if (mSampleTime > 0 && mSpeed > 0) {
+            mRemainingTime = ((mBatchSize - mBytesCopied) * 1000) / mSpeed;
+        } else {
+            mRemainingTime = 0;
+        }
+
+        mSampleTime = elapsedTime;
+        mBytesCopiedSample = mBytesCopied;
+    }
+
+    @Override
+    Notification getFailureNotification() {
+        return getFailureNotification(
+                R.plurals.copy_error_notification_title, R.drawable.ic_menu_copy);
+    }
+
+    @Override
+    void run(FileOperationService service) throws RemoteException {
+        mStartTime = elapsedRealtime();
+
+        // Acquire content providers.
+        srcClient = acquireUnstableProviderOrThrow(
+                getContentResolver(),
+                mSrcFiles.get(0).authority);
+        dstClient = acquireUnstableProviderOrThrow(
+                getContentResolver(),
+                stack.peek().authority);
+
+        // client
+        mBatchSize = calculateSize(srcClient, mSrcFiles);
+
+        DocumentInfo srcInfo;
+        DocumentInfo dstInfo;
+        for (int i = 0; i < mSrcFiles.size() && !isCanceled(); ++i) {
+            srcInfo = mSrcFiles.get(i);
+            dstInfo = stack.peek();
+
+            // Guard unsupported recursive operation.
+            if (dstInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstInfo)) {
+                if (DEBUG) Log.d(TAG, "Skipping recursive operation on directory "
+                        + dstInfo.derivedUri);
+                onFileFailed(srcInfo);
+                continue;
+            }
+
+            if (DEBUG) Log.d(TAG,
+                    "Performing op-type:" + type() + " of " + srcInfo.displayName
+                    + " (" + srcInfo.derivedUri + ")" + " to " + dstInfo.displayName
+                    + " (" + dstInfo.derivedUri + ")");
+
+            processDocument(srcInfo, dstInfo);
+        }
+    }
+
+    /**
+     * Logs progress on the current copy operation. Displays/Updates the progress notification.
+     *
+     * @param bytesCopied
+     */
+    private void makeCopyProgress(long bytesCopied) {
+        onBytesCopied(bytesCopied);
+        if (shouldUpdateProgress()) {
+            updateRemainingTimeEstimate();
+            listener.onProgress(this);
+        }
+    }
+
+    /**
+     * Copies a the given document to the given location.
+     *
+     * @param srcInfo DocumentInfos for the documents to copy.
+     * @param dstDirInfo The destination directory.
+     * @param mode The transfer mode (copy or move).
+     * @return True on success, false on failure.
+     * @throws RemoteException
+     */
+    boolean processDocument(DocumentInfo srcInfo, DocumentInfo dstDirInfo) throws RemoteException {
+
+        // TODO: When optimized copy kicks in, we'll not making any progress updates.
+        // For now. Local storage isn't using optimized copy.
+
+        // When copying within the same provider, try to use optimized copying and moving.
+        // If not supported, then fallback to byte-by-byte copy/move.
+        if (srcInfo.authority.equals(dstDirInfo.authority)) {
+            if ((srcInfo.flags & Document.FLAG_SUPPORTS_COPY) != 0) {
+                if (DocumentsContract.copyDocument(srcClient, srcInfo.derivedUri,
+                        dstDirInfo.derivedUri) == null) {
+                    onFileFailed(srcInfo);
+                }
+                return false;
+            }
+        }
+
+        // If we couldn't do an optimized copy...we fall back to vanilla byte copy.
+        return byteCopyDocument(srcInfo, dstDirInfo);
+    }
+
+    boolean byteCopyDocument(DocumentInfo srcInfo, DocumentInfo dstDirInfo)
+            throws RemoteException {
+        final String dstMimeType;
+        final String dstDisplayName;
+
+        // If the file is virtual, but can be converted to another format, then try to copy it
+        // as such format. Also, append an extension for the target mime type (if known).
+        if (srcInfo.isVirtualDocument()) {
+            final String[] streamTypes = getContentResolver().getStreamTypes(
+                    srcInfo.derivedUri, "*/*");
+            if (streamTypes != null && streamTypes.length > 0) {
+                dstMimeType = streamTypes[0];
+                final String extension = MimeTypeMap.getSingleton().
+                        getExtensionFromMimeType(dstMimeType);
+                dstDisplayName = srcInfo.displayName +
+                        (extension != null ? "." + extension : srcInfo.displayName);
+            } else {
+                // The virtual file is not available as any alternative streamable format.
+                // TODO: Log failures.
+                onFileFailed(srcInfo);
+                return false;
+            }
+        } else {
+            dstMimeType = srcInfo.mimeType;
+            dstDisplayName = srcInfo.displayName;
+        }
+
+        // Create the target document (either a file or a directory), then copy recursively the
+        // contents (bytes or children).
+        final Uri dstUri = DocumentsContract.createDocument(dstClient,
+                dstDirInfo.derivedUri, dstMimeType, dstDisplayName);
+        if (dstUri == null) {
+            // If this is a directory, the entire subdir will not be copied over.
+            onFileFailed(srcInfo);
+            return false;
+        }
+
+        DocumentInfo dstInfo = null;
+        try {
+            dstInfo = DocumentInfo.fromUri(getContentResolver(), dstUri);
+        } catch (FileNotFoundException e) {
+            onFileFailed(srcInfo);
+            return false;
+        }
+
+        final boolean success;
+        if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
+            success = copyDirectoryHelper(srcInfo, dstInfo);
+        } else {
+            success = copyFileHelper(srcInfo, dstInfo, dstMimeType);
+        }
+
+        return success;
+    }
+
+    /**
+     * Handles recursion into a directory and copying its contents. Note that in linux terms, this
+     * does the equivalent of "cp src/* dst", not "cp -r src dst".
+     *
+     * @param srcDirInfo Info of the directory to copy from. The routine will copy the directory's
+     *            contents, not the directory itself.
+     * @param dstDirInfo Info of the directory to copy to. Must be created beforehand.
+     * @return True on success, false if some of the children failed to copy.
+     * @throws RemoteException
+     */
+    private boolean copyDirectoryHelper(DocumentInfo srcDirInfo, DocumentInfo dstDirInfo)
+            throws RemoteException {
+        // Recurse into directories. Copy children into the new subdirectory.
+        final String queryColumns[] = new String[] {
+                Document.COLUMN_DISPLAY_NAME,
+                Document.COLUMN_DOCUMENT_ID,
+                Document.COLUMN_MIME_TYPE,
+                Document.COLUMN_SIZE,
+                Document.COLUMN_FLAGS
+        };
+        Cursor cursor = null;
+        boolean success = true;
+        try {
+            // Iterate over srcs in the directory; copy to the destination directory.
+            final Uri queryUri = DocumentsContract.buildChildDocumentsUri(srcDirInfo.authority,
+                    srcDirInfo.documentId);
+            cursor = srcClient.query(queryUri, queryColumns, null, null, null);
+            DocumentInfo srcInfo;
+            while (cursor.moveToNext()) {
+                srcInfo = DocumentInfo.fromCursor(cursor, srcDirInfo.authority);
+                success &= processDocument(srcInfo, dstDirInfo);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+
+        return success;
+    }
+
+    /**
+     * Handles copying a single file.
+     *
+     * @param srcUriInfo Info of the file to copy from.
+     * @param dstUriInfo Info of the *file* to copy to. Must be created beforehand.
+     * @param mimeType Mime type for the target. Can be different than source for virtual files.
+     * @return True on success, false on error.
+     * @throws RemoteException
+     */
+    private boolean copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, String mimeType)
+            throws RemoteException {
+        // Copy an individual file.
+        CancellationSignal canceller = new CancellationSignal();
+        ParcelFileDescriptor srcFile = null;
+        ParcelFileDescriptor dstFile = null;
+        InputStream src = null;
+        OutputStream dst = null;
+
+        boolean success = true;
+        try {
+            // If the file is virtual, but can be converted to another format, then try to copy it
+            // as such format.
+            if (srcInfo.isVirtualDocument()) {
+                final AssetFileDescriptor srcFileAsAsset =
+                        srcClient.openTypedAssetFileDescriptor(
+                                srcInfo.derivedUri, mimeType, null, canceller);
+                srcFile = srcFileAsAsset.getParcelFileDescriptor();
+                src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
+            } else {
+                srcFile = srcClient.openFile(srcInfo.derivedUri, "r", canceller);
+                src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
+            }
+
+            dstFile = dstClient.openFile(dstInfo.derivedUri, "w", canceller);
+            dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile);
+
+            byte[] buffer = new byte[8192];
+            int len;
+            while ((len = src.read(buffer)) != -1) {
+                if (isCanceled()) {
+                    if (DEBUG) Log.d(TAG, "Canceled copy mid-copy. Id:" + id);
+                    success = false;
+                    break;
+                }
+                dst.write(buffer, 0, len);
+                makeCopyProgress(len);
+            }
+
+            srcFile.checkError();
+        } catch (IOException e) {
+            success = false;
+            onFileFailed(srcInfo);
+
+            if (dstFile != null) {
+                try {
+                    dstFile.closeWithError(e.getMessage());
+                } catch (IOException closeError) {
+                    Log.e(TAG, "Error closing destination", closeError);
+                }
+            }
+        } finally {
+            // This also ensures the file descriptors are closed.
+            IoUtils.closeQuietly(src);
+            IoUtils.closeQuietly(dst);
+        }
+
+        if (!success) {
+            // Clean up half-copied files.
+            canceller.cancel();
+            try {
+                DocumentsContract.deleteDocument(dstClient, dstInfo.derivedUri);
+            } catch (RemoteException e) {
+                // RemoteExceptions usually signal that the connection is dead, so there's no
+                // point attempting to continue. Propagate the exception up so the copy job is
+                // cancelled.
+                Log.w(TAG, "Failed to cleanup after copy error: " + srcInfo.derivedUri, e);
+                throw e;
+            }
+        }
+
+        return success;
+    }
+
+    /**
+     * Calculates the cumulative size of all the documents in the list. Directories are recursed
+     * into and totaled up.
+     *
+     * @param srcs
+     * @return Size in bytes.
+     * @throws RemoteException
+     */
+    private static long calculateSize(ContentProviderClient client, List<DocumentInfo> srcs)
+            throws RemoteException {
+        long result = 0;
+
+        for (DocumentInfo src : srcs) {
+            if (src.isDirectory()) {
+                // Directories need to be recursed into.
+                result += calculateFileSizesRecursively(client, src.derivedUri);
+            } else {
+                result += src.size;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Calculates (recursively) the cumulative size of all the files under the given directory.
+     *
+     * @throws RemoteException
+     */
+    private static long calculateFileSizesRecursively(
+            ContentProviderClient client, Uri uri) throws RemoteException {
+        final String authority = uri.getAuthority();
+        final Uri queryUri = DocumentsContract.buildChildDocumentsUri(authority,
+                DocumentsContract.getDocumentId(uri));
+        final String queryColumns[] = new String[] {
+                Document.COLUMN_DOCUMENT_ID,
+                Document.COLUMN_MIME_TYPE,
+                Document.COLUMN_SIZE
+        };
+
+        long result = 0;
+        Cursor cursor = null;
+        try {
+            cursor = client.query(queryUri, queryColumns, null, null, null);
+            while (cursor.moveToNext()) {
+                if (Document.MIME_TYPE_DIR.equals(
+                        getCursorString(cursor, Document.COLUMN_MIME_TYPE))) {
+                    // Recurse into directories.
+                    final Uri dirUri = DocumentsContract.buildDocumentUri(authority,
+                            getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
+                    result += calculateFileSizesRecursively(client, dirUri);
+                } else {
+                    // This may return -1 if the size isn't defined. Ignore those cases.
+                    long size = getCursorLong(cursor, Document.COLUMN_SIZE);
+                    result += size > 0 ? size : 0;
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+
+        return result;
+    }
+
+    @Override
+    void cleanup() {
+        ContentProviderClient.releaseQuietly(srcClient);
+        ContentProviderClient.releaseQuietly(dstClient);
+    }
+
+    /**
+     * Returns true if {@code doc} is a descendant of {@code parentDoc}.
+     * @throws RemoteException
+     */
+    boolean isDescendentOf(DocumentInfo doc, DocumentInfo parentDoc)
+            throws RemoteException {
+        if (parentDoc.isDirectory() && doc.authority.equals(parentDoc.authority)) {
+            return DocumentsContract.isChildDocument(
+                    dstClient, doc.derivedUri, parentDoc.derivedUri);
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
new file mode 100644
index 0000000..6d87ecf
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperationService.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2015 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.documentsui.services;
+
+import static android.os.SystemClock.elapsedRealtime;
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.IntDef;
+import android.app.IntentService;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.documentsui.Shared;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+
+import com.google.common.base.Objects;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileOperationService extends IntentService implements Job.Listener {
+    public static final String TAG = "FileOperationService";
+
+    public static final String EXTRA_JOB_ID = "com.android.documentsui.JOB_ID";
+    public static final String EXTRA_OPERATION = "com.android.documentsui.OPERATION";
+    public static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
+    public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
+    public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
+
+    public static final int OPERATION_UNKNOWN = -1;
+    public static final int OPERATION_COPY = 1;
+    public static final int OPERATION_MOVE = 2;
+    public static final int OPERATION_DELETE = 3;
+
+    @IntDef(flag = true, value = {
+            OPERATION_UNKNOWN,
+            OPERATION_COPY,
+            OPERATION_MOVE,
+            OPERATION_DELETE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface OpType {}
+
+    // TODO: Move it to a shared file when more operations are implemented.
+    public static final int FAILURE_COPY = 1;
+
+    private PowerManager mPowerManager;
+
+    private NotificationManager mNotificationManager;
+
+    // TODO: Rework service to support multiple concurrent jobs.
+    private volatile Job mJob;
+
+    // For testing only.
+    @Nullable private TestOnlyListener mJobFinishedListener;
+
+    public FileOperationService() {
+        super("FileOperationService");
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        if (DEBUG) Log.d(TAG, "Created.");
+        mPowerManager = getSystemService(PowerManager.class);
+        mNotificationManager = getSystemService(NotificationManager.class);
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (DEBUG) Log.d(TAG, "onStartCommand: " + intent);
+        if (intent.hasExtra(EXTRA_CANCEL)) {
+            handleCancel(intent);
+            return START_REDELIVER_INTENT;
+        } else {
+            return super.onStartCommand(intent, flags, startId);
+        }
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (DEBUG) Log.d(TAG, "onHandleIntent: " + intent);
+
+        String jobId = intent.getStringExtra(EXTRA_JOB_ID);
+        @OpType int operationType = intent.getIntExtra(EXTRA_OPERATION, OPERATION_UNKNOWN);
+        checkArgument(jobId != null);
+        if (intent.hasExtra(EXTRA_CANCEL)) {
+            handleCancel(intent);
+            return;
+        }
+
+        checkArgument(operationType != OPERATION_UNKNOWN);
+
+        PowerManager.WakeLock wakeLock = mPowerManager.newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
+        DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
+
+        Job job = createJob(operationType, jobId, srcs, stack);
+
+        try {
+            wakeLock.acquire();
+
+            mNotificationManager.notify(job.id, 0, job.getSetupNotification());
+            job.run(this);
+
+        } catch (Exception e) {
+            // Catch-all to prevent any copy errors from wedging the app.
+            Log.e(TAG, "Exceptions occurred during copying", e);
+        } finally {
+            if (DEBUG) Log.d(TAG, "Cleaning up after copy");
+
+            job.cleanup();
+            wakeLock.release();
+
+            // Dismiss the ongoing copy notification when the copy is done.
+            mNotificationManager.cancel(job.id, 0);
+
+            if (job.failed()) {
+                Log.e(TAG, job.failedFiles.size() + " files failed to copy");
+                mNotificationManager.notify(job.id, 0, job.getFailureNotification());
+            }
+
+            // TEST ONLY CODE...<raised eyebrows>
+            if (mJobFinishedListener != null) {
+                mJobFinishedListener.onFinished(job.failedFiles);
+            }
+
+            deleteJob(job);
+            if (DEBUG) Log.d(TAG, "Done cleaning up");
+        }
+    }
+
+    /**
+     * Cancels the operation corresponding to job id, identified in "EXTRA_JOB_ID".
+     *
+     * @param intent The cancellation intent.
+     */
+    private void handleCancel(Intent intent) {
+        checkArgument(intent.hasExtra(EXTRA_CANCEL));
+        String jobId = checkNotNull(intent.getStringExtra(EXTRA_JOB_ID));
+
+        // Do nothing if the cancelled ID doesn't match the current job ID. This prevents racey
+        // cancellation requests from affecting unrelated copy jobs.  However, if the current job ID
+        // is null, the service most likely crashed and was revived by the incoming cancel intent.
+        // In that case, always allow the cancellation to proceed.
+        if (mJob != null && Objects.equal(jobId, mJob.id)) {
+            mJob.cancel();
+        }
+
+        // Dismiss the progress notification here rather than in the copy loop. This preserves
+        // interactivity for the user in case the copy loop is stalled.
+        // Try to cancel it even if we don't have a job id...in case there is some sad
+        // orphan notification.
+        mNotificationManager.cancel(jobId, 0);
+    }
+
+    public static String createJobId() {
+        return String.valueOf(elapsedRealtime());
+    }
+
+    Job createJob(
+            @OpType int operationType, String id, ArrayList<DocumentInfo> srcs,
+            DocumentStack stack) {
+
+        checkState(mJob == null);
+
+        switch (operationType) {
+            case OPERATION_COPY:
+                mJob = new CopyJob(this, getApplicationContext(), this, id, stack, srcs);
+                break;
+            case OPERATION_MOVE:
+                mJob = new MoveJob(this, getApplicationContext(), this, id, stack, srcs);
+                break;
+            case OPERATION_DELETE:
+                throw new UnsupportedOperationException();
+            default:
+                throw new UnsupportedOperationException();
+        }
+
+        return checkNotNull(mJob);
+    }
+
+    void deleteJob(Job job) {
+        checkArgument(job == mJob);
+        mJob = null;
+    }
+
+    @Override
+    public void onProgress(CopyJob job) {
+        if (DEBUG) Log.d(TAG, "On copy progress...");
+        mNotificationManager.notify(job.id, 0, job.getProgressNotification());
+    }
+
+    @Override
+    public void onProgress(MoveJob job) {
+        if (DEBUG) Log.d(TAG, "On move progress...");
+        mNotificationManager.notify(job.id, 0, job.getProgressNotification());
+    }
+
+    /**
+     * Sets a callback to be run when the next run job is finished.
+     * This is test ONLY instrumentation. The alternative is for us to add
+     * broadcast intents SOLELY for the purpose of testing.
+     * @param listener
+     */
+    @VisibleForTesting
+    void addFinishedListener(TestOnlyListener listener) {
+        this.mJobFinishedListener = listener;
+    }
+
+    /**
+     * Only used for testing. Is that obvious enough?
+     */
+    @VisibleForTesting
+    interface TestOnlyListener {
+        void onFinished(List<DocumentInfo> failed);
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
new file mode 100644
index 0000000..88bf03b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
@@ -0,0 +1,190 @@
+/*
+ * 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.documentsui.services;
+
+import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.Shared.EXTRA_STACK;
+import static com.android.documentsui.Shared.asArrayList;
+import static com.android.documentsui.Shared.getQuantityString;
+import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL;
+import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID;
+import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION;
+import static com.android.documentsui.services.FileOperationService.EXTRA_SRC_LIST;
+import static com.android.documentsui.services.FileOperationService.OPERATION_COPY;
+import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE;
+import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Parcelable;
+import android.support.design.widget.Snackbar;
+import android.util.Log;
+
+import com.android.documentsui.R;
+import com.android.documentsui.Snackbars;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.services.FileOperationService.OpType;
+
+import java.util.List;
+
+/**
+ * Helper functions for starting various file operations.
+ */
+public final class FileOperations {
+
+    private static final String TAG = "FileOperations";
+
+    private FileOperations() {}
+
+    /**
+     * Tries to start the activity. Returns the job id.
+     */
+    public static String start(
+            Activity activity, List<DocumentInfo> srcDocs, DocumentStack stack,
+            int operationType) {
+
+        if (DEBUG) Log.d(TAG, "Handling generic 'start' call.");
+
+        switch (operationType) {
+            case OPERATION_COPY:
+                return FileOperations.copy(activity, srcDocs, stack);
+            case OPERATION_MOVE:
+                return FileOperations.move(activity, srcDocs, stack);
+            case OPERATION_DELETE:
+                return FileOperations.delete(activity, srcDocs, stack);
+            default:
+                throw new UnsupportedOperationException("Unknown operation: " + operationType);
+        }
+    }
+
+    /**
+     * Makes a best effort to cancel operation identified by jobId.
+     *
+     * @param context Context for the intent.
+     * @param jobId The id of the job to cancel.
+     *     Use {@link FileOperationService#createJobId} if you don't have one handy.
+     * @param srcDocs A list of src files to copy.
+     * @param dstStack The copy destination stack.
+     */
+    public static void cancel(Activity activity, String jobId) {
+        if (DEBUG) Log.d(TAG, "Attempting to canceling operation: " + jobId);
+
+        Intent intent = new Intent(activity, FileOperationService.class);
+        intent.putExtra(EXTRA_CANCEL, true);
+        intent.putExtra(EXTRA_JOB_ID, jobId);
+
+        activity.startService(intent);
+    }
+
+    /**
+     * Starts the service for a copy operation.
+     *
+     * @param context Context for the intent.
+     * @param jobId A unique jobid for this job.
+     *     Use {@link FileOperationService#createJobId} if you don't have one handy.
+     * @param srcDocs A list of src files to copy.
+     * @param destination The copy destination stack.
+     */
+    public static String copy(
+            Activity activity, List<DocumentInfo> srcDocs, DocumentStack destination) {
+        String jobId = FileOperationService.createJobId();
+        if (DEBUG) Log.d(TAG, "Initiating 'copy' operation id: " + jobId);
+
+        Intent intent = createBaseIntent(OPERATION_COPY, activity, jobId, srcDocs, destination);
+
+        createSharedSnackBar(activity, R.plurals.copy_begin, srcDocs.size())
+                .show();
+
+        activity.startService(intent);
+
+        return jobId;
+    }
+
+    /**
+     * Starts the service for a move operation.
+     *
+     * @param jobId A unique jobid for this job.
+     *     Use {@link FileOperationService#createJobId} if you don't have one handy.
+     * @param srcDocs A list of src files to copy.
+     * @param destination The move destination stack.
+     */
+    public static String move(
+            Activity activity, List<DocumentInfo> srcDocs, DocumentStack destination) {
+        String jobId = FileOperationService.createJobId();
+        if (DEBUG) Log.d(TAG, "Initiating 'move' operation id: " + jobId);
+
+        Intent intent = createBaseIntent(OPERATION_MOVE, activity, jobId, srcDocs, destination);
+
+        createSharedSnackBar(activity, R.plurals.move_begin, srcDocs.size())
+                .show();
+
+        activity.startService(intent);
+
+        return jobId;
+    }
+
+    /**
+     * Starts the service for a move operation.
+     *
+     * @param jobId A unique jobid for this job.
+     *     Use {@link FileOperationService#createJobId} if you don't have one handy.
+     * @param srcDocs A list of src files to copy.
+     * @return Id of the job.
+     */
+    public static String delete(
+            Activity activity, List<DocumentInfo> srcDocs, DocumentStack location) {
+        String jobId = FileOperationService.createJobId();
+        if (DEBUG) Log.d(TAG, "Initiating 'delete' operation id: " + jobId);
+
+        Intent intent = createBaseIntent(OPERATION_DELETE, activity, jobId, srcDocs, location);
+        activity.startService(intent);
+
+        return jobId;
+    }
+
+    /**
+     * Starts the service for a move operation.
+     *
+     * @param jobId A unique jobid for this job.
+     *     Use {@link FileOperationService#createJobId} if you don't have one handy.
+     * @param srcDocs A list of src files to copy.
+     * @return Id of the job.
+     */
+    public static Intent createBaseIntent(
+            @OpType int operationType, Activity activity, String jobId,
+            List<DocumentInfo> srcDocs, DocumentStack localeStack) {
+
+        Intent intent = new Intent(activity, FileOperationService.class);
+        intent.putExtra(EXTRA_JOB_ID, jobId);
+        intent.putParcelableArrayListExtra(
+                EXTRA_SRC_LIST, asArrayList(srcDocs));
+        intent.putExtra(EXTRA_STACK, (Parcelable) localeStack);
+        intent.putExtra(EXTRA_OPERATION, operationType);
+
+        return intent;
+    }
+
+    private static Snackbar createSharedSnackBar(Activity activity, int contentId, int fileCount) {
+        Resources res = activity.getResources();
+        return Snackbars.makeSnackbar(
+                activity,
+                getQuantityString(activity, contentId, fileCount),
+                Snackbar.LENGTH_SHORT);
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/Job.java b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
new file mode 100644
index 0000000..5c37a87
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/Job.java
@@ -0,0 +1,193 @@
+/*
+ * 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.documentsui.services;
+
+import static com.android.documentsui.services.FileOperationService.OPERATION_UNKNOWN;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.DrawableRes;
+import android.annotation.PluralsRes;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.provider.DocumentsContract;
+
+import com.android.documentsui.FilesActivity;
+import com.android.documentsui.R;
+import com.android.documentsui.Shared;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.services.FileOperationService.OpType;
+
+import java.util.ArrayList;
+
+abstract class Job {
+
+    final Context serviceContext;
+    final Context appContext;
+    final Listener listener;
+
+    final @OpType int mOpType;
+    final String id;
+    final DocumentStack stack;
+
+    final ArrayList<DocumentInfo> failedFiles = new ArrayList<>();
+    final Notification.Builder mProgressBuilder;
+
+    private volatile boolean mCanceled;
+
+    /**
+     * A simple progressable job, much like an AsyncTask, but with support
+     * for providing various related notification, progress and navigation information.
+     * @param opType
+     *
+     * @param serviceContext The context of the service in which this job is running.
+     *     This is usually just "this".
+     * @param appContext The context of the invoking application. This is usually
+     *     just {@code getApplicationContext()}.
+     * @param listener
+     * @param id Arbitrary string ID
+     * @param stack The documents stack context relating to this request. This is the
+     *     destination in the Files app where the user will be take when the
+     *     navigation intent is invoked (presumably from notification).
+     */
+    Job(@OpType int opType, Context serviceContext, Context appContext, Listener listener,
+            String id, DocumentStack stack) {
+
+        checkArgument(opType != OPERATION_UNKNOWN);
+        this.serviceContext = serviceContext;
+        this.appContext = appContext;
+        this.listener = listener;
+        mOpType = opType;
+
+        this.id = id;
+        this.stack = stack;
+
+        mProgressBuilder = createProgressBuilder();
+    }
+
+    abstract void run(FileOperationService service) throws RemoteException;
+    abstract void cleanup();
+
+    @OpType int type() {
+        return mOpType;
+    }
+
+    abstract Notification getSetupNotification();
+    // TODO: Progress notification for deletes.
+    // abstract Notification getProgressNotification(long bytesCopied);
+    abstract Notification getFailureNotification();
+
+    final void cancel() {
+        mCanceled = true;
+    }
+
+    final boolean isCanceled() {
+        return mCanceled;
+    }
+
+    final ContentResolver getContentResolver() {
+        return serviceContext.getContentResolver();
+    }
+
+    void onFileFailed(DocumentInfo file) {
+        failedFiles.add(file);
+    }
+
+    final boolean failed() {
+        return !failedFiles.isEmpty();
+    }
+
+    Notification getSetupNotification(String content) {
+        mProgressBuilder.setProgress(0, 0, true);
+        mProgressBuilder.setContentText(content);
+        return mProgressBuilder.build();
+    }
+
+    Notification getFailureNotification(@PluralsRes int titleId, @DrawableRes int icon) {
+        final Intent navigateIntent = buildNavigateIntent();
+        navigateIntent.putExtra(FileOperationService.EXTRA_FAILURE, FileOperationService.FAILURE_COPY);
+        navigateIntent.putExtra(FileOperationService.EXTRA_OPERATION, mOpType);
+
+        navigateIntent.putParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST, failedFiles);
+
+        final Notification.Builder errorBuilder = new Notification.Builder(serviceContext)
+                .setContentTitle(serviceContext.getResources().getQuantityString(titleId,
+                        failedFiles.size(), failedFiles.size()))
+                .setContentText(serviceContext.getString(R.string.notification_touch_for_details))
+                .setContentIntent(PendingIntent.getActivity(appContext, 0, navigateIntent,
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT))
+                .setCategory(Notification.CATEGORY_ERROR)
+                .setSmallIcon(icon)
+                .setAutoCancel(true);
+        return errorBuilder.build();
+    }
+
+    abstract Builder createProgressBuilder();
+
+    final Builder createProgressBuilder(
+            String title, @DrawableRes int icon,
+            String actionTitle, @DrawableRes int actionIcon) {
+        Notification.Builder progressBuilder = new Notification.Builder(serviceContext)
+                .setContentTitle(title)
+                .setContentIntent(
+                        PendingIntent.getActivity(appContext, 0, buildNavigateIntent(), 0))
+                .setCategory(Notification.CATEGORY_PROGRESS)
+                .setSmallIcon(icon)
+                .setOngoing(true);
+
+        final Intent cancelIntent = createCancelIntent();
+
+        progressBuilder.addAction(
+                actionIcon,
+                actionTitle,
+                PendingIntent.getService(
+                        serviceContext,
+                        0,
+                        cancelIntent,
+                        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT));
+
+        return progressBuilder;
+    }
+
+    /**
+     * Creates an intent for navigating back to the destination directory.
+     */
+    Intent buildNavigateIntent() {
+        Intent intent = new Intent(serviceContext, FilesActivity.class);
+        intent.setAction(DocumentsContract.ACTION_BROWSE);
+        intent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
+        return intent;
+    }
+
+    Intent createCancelIntent() {
+        final Intent cancelIntent = new Intent(serviceContext, FileOperationService.class);
+        cancelIntent.putExtra(FileOperationService.EXTRA_CANCEL, true);
+        cancelIntent.putExtra(FileOperationService.EXTRA_JOB_ID, id);
+        return cancelIntent;
+    }
+
+    interface Listener {
+        void onProgress(CopyJob job);
+        void onProgress(MoveJob job);
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
new file mode 100644
index 0000000..4817f58
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -0,0 +1,124 @@
+/*
+ * 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.documentsui.services;
+
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.content.Context;
+import android.os.RemoteException;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.util.Log;
+
+import com.android.documentsui.R;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+
+import java.util.List;
+
+final class MoveJob extends CopyJob {
+
+    private static final String TAG = "MoveJob";
+
+    /**
+     * Moves files to a destination identified by {@code destination}.
+     * Performs most work by delegating to CopyJob, then deleting
+     * a file after it has been copied.
+     *
+     * @see @link {@link Job} constructor for most param descriptions.
+     *
+     * @param srcs List of files to be moved.
+     */
+    MoveJob(Context serviceContext, Context appContext, Listener listener,
+            String id, DocumentStack destination, List<DocumentInfo> srcs) {
+        super(serviceContext, appContext, listener, id, destination, srcs);
+    }
+
+    @Override
+    int type() {
+        return FileOperationService.OPERATION_MOVE;
+    }
+
+    @Override
+    Builder createProgressBuilder() {
+        return super.createProgressBuilder(
+                serviceContext.getString(R.string.move_notification_title),
+                R.drawable.ic_menu_copy,
+                serviceContext.getString(android.R.string.cancel),
+                R.drawable.ic_cab_cancel);
+    }
+
+    @Override
+    public Notification getSetupNotification() {
+        return getSetupNotification(serviceContext.getString(R.string.move_preparing));
+    }
+
+    @Override
+    public Notification getProgressNotification() {
+        return getProgressNotification(R.string.copy_preparing);
+    }
+
+    @Override
+    Notification getFailureNotification() {
+        return getFailureNotification(
+                R.plurals.move_error_notification_title, R.drawable.ic_menu_copy);
+    }
+
+    /**
+     * Copies a the given document to the given location.
+     *
+     * @param srcInfo DocumentInfos for the documents to copy.
+     * @param dstDirInfo The destination directory.
+     * @param mode The transfer mode (copy or move).
+     * @return True on success, false on failure.
+     * @throws RemoteException
+     */
+    @Override
+    boolean processDocument(DocumentInfo srcInfo, DocumentInfo dstDirInfo) throws RemoteException {
+
+        // TODO: When optimized copy kicks in, we're not making any progress updates. FIX IT!
+
+        // When copying within the same provider, try to use optimized copying and moving.
+        // If not supported, then fallback to byte-by-byte copy/move.
+        if (srcInfo.authority.equals(dstDirInfo.authority)) {
+            if ((srcInfo.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
+                if (DocumentsContract.moveDocument(srcClient, srcInfo.derivedUri,
+                        dstDirInfo.derivedUri) == null) {
+                    onFileFailed(srcInfo);
+                }
+                return false;
+            }
+        }
+
+        // If we couldn't do an optimized copy...we fall back to vanilla byte copy.
+        boolean success = byteCopyDocument(srcInfo, dstDirInfo);
+
+        if (success) {
+            // This is racey. We should make sure that we never delete a directory after
+            // it changed, so we don't remove a file which had not been copied earlier
+            // to the target location.
+            try {
+                DocumentsContract.deleteDocument(srcClient, srcInfo.derivedUri);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to delete source after copy: " + srcInfo.derivedUri, e);
+                return false;
+            }
+        }
+
+        return success;
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
new file mode 100644
index 0000000..baa7a2e
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
@@ -0,0 +1,293 @@
+/*
+ * 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.documentsui;
+
+import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StubProvider.ROOT_0_ID;
+import static com.android.documentsui.StubProvider.ROOT_1_ID;
+
+import android.app.Instrumentation;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.documentsui.model.RootInfo;
+
+@LargeTest
+public class SearchViewUiTest extends InstrumentationTestCase {
+
+    private static final int TIMEOUT = 5000;
+    private static final String TAG = "SearchViewUiTest";
+    private static final String TARGET_PKG = "com.android.documentsui";
+    private static final String LAUNCHER_PKG = "com.android.launcher";
+
+    private UiBot mBot;
+    private UiDevice mDevice;
+    private Context mContext;
+    private ContentResolver mResolver;
+    private DocumentsProviderHelper mDocsHelper;
+    private ContentProviderClient mClient;
+    private RootInfo mRoot_0;
+    private RootInfo mRoot_1;
+
+    private UiObject mSearchView;
+    private UiObject mSearchTextField;
+    private UiObject mDocsList;
+    private UiObject mMessageTextView;
+    private UiObject mSearchIcon;
+
+    public void setUp() throws Exception {
+        // Initialize UiDevice instance.
+        Instrumentation instrumentation = getInstrumentation();
+
+        mDevice = UiDevice.getInstance(instrumentation);
+
+        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+
+        // Start from the home screen.
+        mDevice.pressHome();
+        mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
+
+        // NOTE: Must be the "target" context, else security checks in content provider will fail.
+        mContext = instrumentation.getTargetContext();
+        mResolver = mContext.getContentResolver();
+
+        mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
+        mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
+
+        // Launch app.
+        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(intent);
+        // Wait for the app to appear.
+        mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
+        mDevice.waitForIdle();
+
+        mBot = new UiBot(mDevice, TIMEOUT);
+
+        resetStorage(); // Just incase a test failed and tearDown didn't happen.
+
+        initUiObjects();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mDevice.pressBack();
+        resetStorage();
+        mClient.release();
+    }
+
+    private void resetStorage() throws RemoteException {
+        mClient.call("clear", null, null);
+        // TODO: Would be nice to have an event to wait on here.
+        mDevice.waitForIdle();
+    }
+
+    private void initTestFiles() throws RemoteException {
+        mRoot_0 = mDocsHelper.getRoot(ROOT_0_ID);
+        mRoot_1 = mDocsHelper.getRoot(ROOT_1_ID);
+
+        mDocsHelper.createDocument(mRoot_0, "text/plain", "file10.log");
+        mDocsHelper.createDocument(mRoot_0, "image/png", "file1.png");
+        mDocsHelper.createDocument(mRoot_0, "text/csv", "file2.csv");
+
+        mDocsHelper.createDocument(mRoot_1, "text/plain", "anotherFile0.log");
+        mDocsHelper.createDocument(mRoot_1, "text/plain", "poodles.text");
+    }
+
+    private void initUiObjects() {
+        mSearchView = mBot.findSearchView();
+        mSearchTextField = mBot.findSearchViewTextField();
+        mDocsList = mBot.findDocumentsList();
+        mMessageTextView = mBot.findMessageTextView();
+        mSearchIcon = mBot.findSearchViewIcon();
+    }
+
+    public void testSearchViewExpandsOnClick() throws Exception {
+        assertTrue(mSearchIcon.exists());
+        assertFalse(mSearchTextField.exists());
+
+        mSearchView.click();
+
+        assertTrue(mSearchTextField.exists());
+        assertTrue(mSearchTextField.isFocused());
+        assertFalse(mSearchIcon.exists());
+    }
+
+    public void testSearchViewCollapsesOnBack() throws Exception {
+        assertTrue(mSearchIcon.exists());
+        assertFalse(mSearchTextField.exists());
+
+        mSearchView.click();
+
+        mDevice.pressBack();
+
+        assertTrue(mSearchIcon.exists());
+        assertFalse(mSearchTextField.exists());
+    }
+
+    public void testSearchViewClearsTextOnBack() throws Exception {
+        assertTrue(mSearchIcon.exists());
+        assertFalse(mSearchTextField.exists());
+
+        String query = "file2";
+        mSearchView.click();
+        mSearchTextField.setText(query);
+
+        assertSearchTextField(true, query);
+
+        mDevice.pressBack();
+
+        assertTrue(mSearchIcon.exists());
+        assertFalse(mSearchTextField.exists());
+    }
+
+    public void testSearchFound() throws Exception {
+        initTestFiles();
+
+        mBot.openRoot(ROOT_0_ID);
+
+        assertDefaultTestDir0();
+
+        String query = "file1";
+        mSearchView.click();
+        mSearchTextField.setText(query);
+
+        assertTrue(mDocsList.exists());
+        assertSearchTextField(true, query);
+
+        mDevice.pressEnter();
+
+        assertTrue(mDocsList.exists());
+        assertEquals(2, mDocsList.getChildCount());
+        mBot.assertHasDocuments("file1.png", "file10.log");
+        assertSearchTextField(false, query);
+    }
+
+    public void testSearchFoundClearsOnBack() throws Exception {
+        initTestFiles();
+
+        mBot.openRoot(ROOT_0_ID);
+
+        assertDefaultTestDir0();
+
+        String query = "file1";
+        mSearchView.click();
+        mSearchTextField.setText(query);
+
+        mDevice.pressEnter();
+        mDevice.pressBack();
+
+        assertDefaultTestDir0();
+    }
+
+    public void testSearchNoResults() throws Exception {
+        initTestFiles();
+
+        mBot.openRoot(ROOT_0_ID);
+
+        assertDefaultTestDir0();
+
+        String query = "chocolate";
+        mSearchView.click();
+        mSearchTextField.setText(query);
+
+        mDevice.pressEnter();
+
+        assertFalse(mDocsList.exists());
+        assertTrue(mMessageTextView.exists());
+        assertEquals(mContext.getString(R.string.empty), mMessageTextView.getText());
+        assertSearchTextField(false, query);
+    }
+
+    public void testSearchNoResultsClearsOnBack() throws Exception {
+        initTestFiles();
+
+        mBot.openRoot(ROOT_0_ID);
+
+        assertDefaultTestDir0();
+
+        String query = "chocolate";
+        mSearchView.click();
+        mSearchTextField.setText(query);
+
+        mDevice.pressEnter();
+        mDevice.pressBack();
+
+        assertDefaultTestDir0();
+    }
+
+    public void testSearchFoundClearsDirectoryChange() throws Exception {
+        initTestFiles();
+
+        mBot.openRoot(ROOT_0_ID);
+
+        assertDefaultTestDir0();
+
+        String query = "file1";
+        mSearchView.click();
+        mSearchTextField.setText(query);
+
+        mDevice.pressEnter();
+
+        mBot.openRoot(ROOT_1_ID);
+
+        // This assert is failing right now - fix will come with SearchManager refactoring
+        // assertDefaultTestDir1();
+        //
+        // mBot.openRoot(ROOT_0_ID);
+        //
+        // assertDefaultTestDir0();
+    }
+
+    private void assertDefaultTestDir0() throws UiObjectNotFoundException {
+        assertTrue(mSearchIcon.exists());
+        assertTrue(mDocsList.exists());
+        assertFalse(mSearchTextField.exists());
+        assertEquals(3, mDocsList.getChildCount());
+        mBot.assertHasDocuments("file2.csv", "file1.png", "file10.log");
+    }
+
+    private void assertDefaultTestDir1() throws UiObjectNotFoundException {
+        assertTrue(mSearchIcon.exists());
+        assertFalse(mSearchTextField.exists());
+        assertTrue(mDocsList.exists());
+        assertEquals(2, mDocsList.getChildCount());
+        mBot.assertHasDocuments("anotherFile0.log", "poodles.txt");
+    }
+
+    private void assertSearchTextField(boolean isFocused, String query)
+            throws UiObjectNotFoundException {
+        assertFalse(mSearchIcon.exists());
+        assertTrue(mSearchTextField.exists());
+        assertEquals(isFocused, mSearchTextField.isFocused());
+        assertEquals(query, mSearchTextField.getText());
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
new file mode 100644
index 0000000..b74b985
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StateTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.documentsui;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.documentsui.model.DocumentInfo;
+
+@SmallTest
+public class StateTest extends AndroidTestCase {
+    public void testPushDocument() {
+        final State state = new State();
+        final DocumentInfo infoFirst = new DocumentInfo();
+        infoFirst.displayName = "firstDirectory";
+        final DocumentInfo infoSecond = new DocumentInfo();
+        infoSecond.displayName = "secondDirectory";
+        assertFalse(state.hasLocationChanged());
+        state.pushDocument(infoFirst);
+        state.pushDocument(infoSecond);
+        assertTrue(state.hasLocationChanged());
+        assertEquals("secondDirectory", state.stack.getFirst().displayName);
+        state.popDocument();
+        assertEquals("firstDirectory", state.stack.getFirst().displayName);
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 2c311a7..fb6445b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -135,7 +135,8 @@
             final RootInfo info = entry.getValue();
             final RowBuilder row = result.newRow();
             row.add(Root.COLUMN_ROOT_ID, id);
-            row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD);
+            row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD
+                    | Root.FLAG_SUPPORTS_SEARCH);
             row.add(Root.COLUMN_TITLE, id);
             row.add(Root.COLUMN_DOCUMENT_ID, info.document.documentId);
             row.add(Root.COLUMN_AVAILABLE_BYTES, info.getRemainingCapacity());
@@ -270,6 +271,29 @@
     }
 
     @Override
+    public Cursor querySearchDocuments(String rootId, String query, String[] projection)
+            throws FileNotFoundException {
+
+        StubDocument parentDocument = mRoots.get(rootId).document;
+        if (parentDocument == null || parentDocument.file.isFile()) {
+            throw new FileNotFoundException();
+        }
+
+        final MatrixCursor result = new MatrixCursor(
+                projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
+
+        for (File file : parentDocument.file.listFiles()) {
+            if (file.getName().toLowerCase().contains(query)) {
+                StubDocument document = mStorage.get(getDocumentIdForFile(file));
+                if (document != null) {
+                    includeDocument(result, document);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
     public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
             throws FileNotFoundException {
         final StubDocument document = mStorage.get(docId);
@@ -316,12 +340,9 @@
             String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
             throws FileNotFoundException {
         final StubDocument document = mStorage.get(documentId);
-        if (document == null || !document.file.isFile()) {
+        if (document == null || !document.file.isFile() || document.streamTypes == null) {
             throw new FileNotFoundException();
         }
-        if ((document.flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) == 0) {
-            throw new IllegalStateException("Tried to open a non-typed document as typed.");
-        }
         for (final String mimeType : document.streamTypes) {
             // Strict compare won't accept wildcards, but that's OK for tests, as DocumentsUI
             // doesn't use them for getStreamTypes nor openTypedDocument.
@@ -349,13 +370,13 @@
             throw new IllegalArgumentException(
                     "The provided Uri is incorrect, or the file is gone.");
         }
-        if ((document.flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) == 0) {
-            return null;
-        }
         if (!"*/*".equals(mimeTypeFilter)) {
             // Not used by DocumentsUI, so don't bother implementing it.
             throw new UnsupportedOperationException();
         }
+        if (document.streamTypes == null) {
+            return null;
+        }
         return document.streamTypes.toArray(new String[document.streamTypes.size()]);
     }
 
@@ -628,9 +649,6 @@
                 File file, String mimeType, List<String> streamTypes, StubDocument parent) {
             int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
                     | Document.FLAG_VIRTUAL_DOCUMENT;
-            if (streamTypes.size() > 0) {
-                flags |= Document.FLAG_SUPPORTS_TYPED_DOCUMENT;
-            }
             return new StubDocument(file, mimeType, streamTypes, flags, parent);
         }
 
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index 68cdf12..c4def8f 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -187,4 +187,41 @@
         mDevice.wait(Until.findObject(selector), mTimeout);
         return mDevice.findObject(selector);
     }
+
+    private UiObject findObject(String resourceId) {
+        final UiSelector object = new UiSelector().resourceId(resourceId);
+        return mDevice.findObject(object);
+    }
+
+    private UiObject findObject(String parentResourceId, String childResourceId) {
+        final UiSelector selector = new UiSelector()
+                .resourceId(parentResourceId)
+                .childSelector(new UiSelector().resourceId(childResourceId));
+        return mDevice.findObject(selector);
+    }
+
+    UiObject findDocumentsList() {
+        return findObject(
+                "com.android.documentsui:id/container_directory",
+                "com.android.documentsui:id/list");
+    }
+
+    UiObject findSearchView() {
+        return findObject("com.android.documentsui:id/menu_search");
+    }
+
+    UiObject findSearchViewTextField() {
+        return findObject("com.android.documentsui:id/menu_search", "android:id/search_src_text");
+    }
+
+    UiObject findSearchViewIcon() {
+        return findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
+    }
+
+    UiObject findMessageTextView() {
+        return findObject(
+                "com.android.documentsui:id/container_directory",
+                "com.android.documentsui:id/message");
+    }
+
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
new file mode 100644
index 0000000..5ce1823
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.support.v7.widget.RecyclerView;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.SparseArray;
+import android.view.ViewGroup;
+
+import com.android.documentsui.State;
+
+import java.util.List;
+
+@SmallTest
+public class ModelBackedDocumentsAdapterTest extends AndroidTestCase {
+
+    private static final String AUTHORITY = "test_authority";
+    private static final String[] NAMES = new String[] {
+            "4",
+            "foo",
+            "1",
+            "bar",
+            "*(Ljifl;a",
+            "0",
+            "baz",
+            "2",
+            "3",
+            "%$%VD"
+    };
+
+    private TestModel mModel;
+    private ModelBackedDocumentsAdapter mAdapter;
+
+    public void setUp() {
+
+        final Context testContext = TestContext.createStorageTestContext(getContext(), AUTHORITY);
+        mModel = new TestModel(testContext, AUTHORITY);
+        mModel.update(NAMES);
+
+        DocumentsAdapter.Environment env = new TestEnvironment(testContext);
+
+        mAdapter = new ModelBackedDocumentsAdapter(
+                env, new IconHelper(testContext, State.MODE_GRID));
+        mAdapter.onModelUpdate(mModel);
+    }
+
+    // Tests that the item count is correct.
+    public void testItemCount() {
+        assertEquals(mModel.getItemCount(), mAdapter.getItemCount());
+    }
+
+    // Tests that the item count is correct.
+    public void testHide_ItemCount() {
+        List<String> ids = mModel.getModelIds();
+        mAdapter.hide(ids.get(0), ids.get(1));
+        assertEquals(mModel.getItemCount() - 2, mAdapter.getItemCount());
+    }
+
+    // Tests that the items can be hidden and unhidden.
+    public void testUnhide_ItemCount() {
+        List<String> ids = mModel.getModelIds();
+        SparseArray<String> hidden = mAdapter.hide(ids.toArray(new String[ids.size()]));
+        mAdapter.unhide(hidden);
+        assertEquals(mModel.getItemCount(), mAdapter.getItemCount());
+    }
+
+    // Tests that the items can be hidden and unhidden.
+    public void testUnhide_PreservesOrder() {
+        List<String> ids = mModel.getModelIds();
+        SparseArray<String> hidden = mAdapter.hide(
+                ids.get(0), ids.get(1), ids.get(5), ids.get(9));
+        mAdapter.unhide(hidden);
+
+        // Finally ensure the restored items are in the original order
+        // by checking them against the model.
+        for (int i = 0; i < mAdapter.getItemCount(); i++) {
+            assertEquals(mModel.idForPosition(i), mAdapter.getModelId(i));
+        }
+    }
+
+    private final class TestEnvironment implements DocumentsAdapter.Environment {
+        private final Context testContext;
+
+        private TestEnvironment(Context testContext) {
+            this.testContext = testContext;
+        }
+
+        @Override
+        public boolean isSelected(String id) {
+            return false;
+        }
+
+        @Override
+        public boolean isDocumentEnabled(String mimeType, int flags) {
+            return true;
+        }
+
+        @Override
+        public void initDocumentHolder(DocumentHolder holder) {}
+
+        @Override
+        public Model getModel() {
+            return mModel;
+        }
+
+        @Override
+        public State getDisplayState() {
+            return null;
+        }
+
+        @Override
+        public Context getContext() {
+            return testContext;
+        }
+
+        @Override
+        public int getColumnCount() {
+            return 4;
+        }
+
+        @Override
+        public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) {}
+    }
+
+    private static class DummyListener implements Model.UpdateListener {
+        public void onModelUpdate(Model model) {}
+        public void onModelUpdateFailed(Exception e) {}
+    }
+
+    private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        public int getItemCount() { return 0; }
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return null;
+        }
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
index 121eb41..a5f0656 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
@@ -21,16 +21,10 @@
 import android.content.ContextWrapper;
 import android.database.Cursor;
 import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
-import android.support.v7.widget.RecyclerView;
 import android.test.AndroidTestCase;
-import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.view.ViewGroup;
 
 import com.android.documentsui.DirectoryResult;
 import com.android.documentsui.RootCursorWrapper;
@@ -40,8 +34,10 @@
 
 import java.util.ArrayList;
 import java.util.BitSet;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
 @SmallTest
@@ -49,31 +45,34 @@
 
     private static final int ITEM_COUNT = 10;
     private static final String AUTHORITY = "test_authority";
+
     private static final String[] COLUMNS = new String[]{
         RootCursorWrapper.COLUMN_AUTHORITY,
         Document.COLUMN_DOCUMENT_ID,
         Document.COLUMN_FLAGS,
         Document.COLUMN_DISPLAY_NAME,
         Document.COLUMN_SIZE,
+        Document.COLUMN_LAST_MODIFIED,
         Document.COLUMN_MIME_TYPE
     };
-    private static Cursor cursor;
 
+    private static final String[] NAMES = new String[] {
+            "4",
+            "foo",
+            "1",
+            "bar",
+            "*(Ljifl;a",
+            "0",
+            "baz",
+            "2",
+            "3",
+            "%$%VD"
+        };
+
+    private Cursor cursor;
     private Context context;
     private Model model;
     private TestContentProvider provider;
-    private static final String[] NAMES = new String[] {
-        "4",
-        "foo",
-        "1",
-        "bar",
-        "*(Ljifl;a",
-        "0",
-        "baz",
-        "2",
-        "3",
-        "%$%VD"
-    };
 
     public void setUp() {
         setupTestContext();
@@ -97,7 +96,7 @@
         r.cursor = cursor;
 
         // Instantiate the model with a dummy view adapter and listener that (for now) do nothing.
-        model = new Model(context, new DummyAdapter());
+        model = new Model(context);
         model.addUpdateListener(new DummyListener());
         model.update(r);
     }
@@ -267,6 +266,43 @@
         assertEquals(ITEM_COUNT, seen.cardinality());
     }
 
+    public void testSort_time() {
+        final int DL_COUNT = 3;
+        MatrixCursor c = new MatrixCursor(COLUMNS);
+        Set<String> currentDownloads = new HashSet<>();
+
+        // Add some files
+        for (int i = 0; i < ITEM_COUNT; i++) {
+            MatrixCursor.RowBuilder row = c.newRow();
+            row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY);
+            row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
+            row.add(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis());
+        }
+        // Add some current downloads (no timestamp)
+        for (int i = ITEM_COUNT; i < ITEM_COUNT + DL_COUNT; i++) {
+            MatrixCursor.RowBuilder row = c.newRow();
+            String id = Integer.toString(i);
+            row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY);
+            row.add(Document.COLUMN_DOCUMENT_ID, id);
+            currentDownloads.add(Model.createModelId(AUTHORITY, id));
+        }
+
+        DirectoryResult r = new DirectoryResult();
+        r.cursor = c;
+        r.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
+        model.update(r);
+
+        List<String> ids = model.getModelIds();
+
+        // Check that all items were accounted for
+        assertEquals(ITEM_COUNT + DL_COUNT, ids.size());
+
+        // Check that active downloads are sorted to the top.
+        for (int i = 0; i < DL_COUNT; i++) {
+            assertTrue(currentDownloads.contains(ids.get(i)));
+        }
+    }
+
     // Tests that Model.delete works correctly.
     public void testDelete() throws Exception {
         // Simulate deleting 2 files.
@@ -326,32 +362,4 @@
         public void onModelUpdate(Model model) {}
         public void onModelUpdateFailed(Exception e) {}
     }
-
-    private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
-        public int getItemCount() { return 0; }
-        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
-        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            return null;
-        }
-    }
-
-    private static class TestContentProvider extends MockContentProvider {
-        List<Uri> mDeleted = new ArrayList<>();
-
-        @Override
-        public Bundle call(String method, String arg, Bundle extras) {
-            // Intercept and log delete method calls.
-            if (DocumentsContract.METHOD_DELETE_DOCUMENT.equals(method)) {
-                final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
-                mDeleted.add(documentUri);
-                return new Bundle();
-            } else {
-                return super.call(method, arg, extras);
-            }
-        }
-
-        public void assertWasDeleted(DocumentInfo doc) {
-            assertTrue(mDeleted.contains(doc.derivedUri));
-        }
-    }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index 5989135..7a3b6d4 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -23,6 +23,7 @@
 
 import com.android.documentsui.TestInputEvent;
 import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
@@ -44,11 +45,13 @@
     private MultiSelectManager mManager;
     private TestCallback mCallback;
     private TestSelectionEnvironment mEnv;
+    private TestDocumentsAdapter mAdapter;
 
     public void setUp() throws Exception {
         mCallback = new TestCallback();
         mEnv = new TestSelectionEnvironment(items);
-        mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_MULTIPLE);
+        mAdapter = new TestDocumentsAdapter(items);
+        mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_MULTIPLE);
         mManager.addCallback(mCallback);
     }
 
@@ -163,8 +166,14 @@
         assertRangeSelection(0, 7);
     }
 
+    public void testKeyboardSelection() {
+        // This simulates shift-navigation.
+        keyToPosition(5, 10, true);
+        assertRangeSelection(5, 10);
+    }
+
     public void testSingleSelectMode() {
-        mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE);
+        mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE);
         mManager.addCallback(mCallback);
         longPress(20);
         tap(13);
@@ -172,7 +181,7 @@
     }
 
     public void testSingleSelectMode_ShiftTap() {
-        mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE);
+        mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE);
         mManager.addCallback(mCallback);
         longPress(13);
         shiftTap(20);
@@ -180,10 +189,10 @@
     }
 
     public void testSingleSelectMode_ShiftDoesNotExtendSelection() {
-        mManager = new MultiSelectManager(mEnv, MultiSelectManager.MODE_SINGLE);
+        mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE);
         mManager.addCallback(mCallback);
         longPress(20);
-        keyToPosition(22, true);
+        keyToPosition(20, 22, true);
         assertSelection(items.get(22));
     }
 
@@ -247,8 +256,8 @@
         mManager.onSingleTapUp(TestInputEvent.shiftClick(position));
     }
 
-    private void keyToPosition(int position, boolean shift) {
-        mManager.attemptChangeFocus(position, shift);
+    private void keyToPosition(int startPos, int endPos, boolean shift) {
+        mManager.changeFocus(startPos, endPos, shift);
     }
 
     private void assertSelected(String... expected) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
index 3d6923f..5c04db9 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java
@@ -20,7 +20,6 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView.AdapterDataObserver;
 import android.support.v7.widget.RecyclerView.OnScrollListener;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -28,7 +27,7 @@
 
 import com.android.documentsui.dirlist.MultiSelectManager.GridModel;
 
-import java.util.List;
+import java.util.ArrayList;
 import java.util.Set;
 
 @SmallTest
@@ -38,15 +37,28 @@
     private static final int CHILD_VIEW_EDGE_PX = 100;
     private static final int VIEWPORT_HEIGHT = 500;
 
-    private static GridModel model;
-    private static TestEnvironment env;
-    private static Set<String> lastSelection;
-    private static int viewWidth;
+    private GridModel model;
+    private TestEnvironment env;
+    private TestDocumentsAdapter adapter;
+    private Set<String> lastSelection;
+    private int viewWidth;
 
-    private static void setUp(int numChildren, int numColumns) {
+    private void initData(final int numChildren, int numColumns) {
         env = new TestEnvironment(numChildren, numColumns);
+        adapter = new TestDocumentsAdapter(new ArrayList<String>()) {
+            @Override
+            public String getModelId(int position) {
+                return Integer.toString(position);
+            }
+
+            @Override
+            public int getItemCount() {
+                return numChildren;
+            }
+        };
+
         viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
-        model = new GridModel(env);
+        model = new GridModel(env, adapter);
         model.addOnSelectionChangedListener(
                 new GridModel.OnSelectionChangedListener() {
                     @Override
@@ -64,7 +76,7 @@
     }
 
     public void testSelectionLeftOfItems() {
-        setUp(20, 5);
+        initData(20, 5);
         model.startSelection(new Point(0, 10));
         model.resizeSelection(new Point(1, 11));
         assertSelected();
@@ -72,7 +84,7 @@
     }
 
     public void testSelectionRightOfItems() {
-        setUp(20, 4);
+        initData(20, 4);
         model.startSelection(new Point(viewWidth - 1, 10));
         model.resizeSelection(new Point(viewWidth - 2, 11));
         assertSelected();
@@ -80,7 +92,7 @@
     }
 
     public void testSelectionAboveItems() {
-        setUp(20, 4);
+        initData(20, 4);
         model.startSelection(new Point(10, 0));
         model.resizeSelection(new Point(11, 1));
         assertSelected();
@@ -88,7 +100,7 @@
     }
 
     public void testSelectionBelowItems() {
-        setUp(5, 4);
+        initData(5, 4);
         model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
         model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
         assertSelected();
@@ -96,7 +108,7 @@
     }
 
     public void testVerticalSelectionBetweenItems() {
-        setUp(20, 4);
+        initData(20, 4);
         model.startSelection(new Point(106, 0));
         model.resizeSelection(new Point(107, 200));
         assertSelected();
@@ -104,7 +116,7 @@
     }
 
     public void testHorizontalSelectionBetweenItems() {
-        setUp(20, 4);
+        initData(20, 4);
         model.startSelection(new Point(0, 105));
         model.resizeSelection(new Point(200, 106));
         assertSelected();
@@ -112,7 +124,7 @@
     }
 
     public void testGrowingAndShrinkingSelection() {
-        setUp(20, 4);
+        initData(20, 4);
         model.startSelection(new Point(0, 0));
         model.resizeSelection(new Point(5, 5));
         assertSelected(0);
@@ -142,7 +154,7 @@
     }
 
     public void testSelectionMovingAroundOrigin() {
-        setUp(16, 4);
+        initData(16, 4);
         model.startSelection(new Point(210, 210));
         model.resizeSelection(new Point(viewWidth - 1, 0));
         assertSelected(2, 3, 6, 7);
@@ -156,7 +168,7 @@
     }
 
     public void testScrollingBandSelect() {
-        setUp(40, 4);
+        initData(40, 4);
         model.startSelection(new Point(0, 0));
         model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
         assertSelected(0, 4, 8, 12, 16);
@@ -173,14 +185,14 @@
         assertEquals(0, model.getPositionNearestOrigin());
     }
 
-    private static void assertSelected(int... selectedPositions) {
+    private void assertSelected(int... selectedPositions) {
         assertEquals(selectedPositions.length, lastSelection.size());
         for (int position : selectedPositions) {
             assertTrue(lastSelection.contains(Integer.toString(position)));
         }
     }
 
-    private static void scroll(int dy) {
+    private void scroll(int dy) {
         assertTrue(env.verticalOffset + VIEWPORT_HEIGHT + dy <= env.getTotalHeight());
         env.verticalOffset += dy;
         model.onScrolled(null, 0, dy);
@@ -322,30 +334,5 @@
         public void focusItem(int i) {
             throw new UnsupportedOperationException();
         }
-
-        @Override
-        public String getModelIdFromAdapterPosition(int position) {
-            return Integer.toString(position);
-        }
-
-        @Override
-        public int getItemCount() {
-            return mNumChildren;
-        }
-
-        @Override
-        public List<String> getModelIds() {
-            return null;
-        }
-
-        @Override
-        public void notifyItemChanged(String id) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void registerDataObserver(AdapterDataObserver observer) {
-            throw new UnsupportedOperationException();
-        }
     }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapperTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapperTest.java
new file mode 100644
index 0000000..398885c
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapperTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.documentsui.dirlist;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.DocumentsContract.Document;
+import android.support.v7.widget.RecyclerView;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.SparseArray;
+import android.view.ViewGroup;
+
+import com.android.documentsui.DirectoryResult;
+import com.android.documentsui.RootCursorWrapper;
+import com.android.documentsui.State;
+
+import java.util.List;
+
+@SmallTest
+public class SectionBreakDocumentsAdapterWrapperTest extends AndroidTestCase {
+
+    private static final String AUTHORITY = "test_authority";
+    private static final String[] NAMES = new String[] {
+            "4",
+            "foo",
+            "1",
+            "bar",
+            "*(Ljifl;a",
+            "0",
+            "baz",
+            "2",
+            "3",
+            "%$%VD"
+    };
+
+    private TestModel mModel;
+    private SectionBreakDocumentsAdapterWrapper mAdapter;
+
+    public void setUp() {
+
+        final Context testContext = TestContext.createStorageTestContext(getContext(), AUTHORITY);
+        DocumentsAdapter.Environment env = new TestEnvironment(testContext);
+
+        mModel = new TestModel(testContext, AUTHORITY);
+        mAdapter = new SectionBreakDocumentsAdapterWrapper(
+            env,
+            new ModelBackedDocumentsAdapter(
+                env, new IconHelper(testContext, State.MODE_GRID)));
+
+        mModel.addUpdateListener(mAdapter);
+    }
+
+    // Tests that the item count is correct for a directory containing only subdirs.
+    public void testItemCount_allDirs() {
+        MatrixCursor c = new MatrixCursor(TestModel.COLUMNS);
+
+        for (int i = 0; i < 5; ++i) {
+            MatrixCursor.RowBuilder row = c.newRow();
+            row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY);
+            row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
+            row.add(Document.COLUMN_SIZE, i);
+            row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+        }
+        DirectoryResult r = new DirectoryResult();
+        r.cursor = c;
+        r.sortOrder = State.SORT_ORDER_SIZE;
+        mModel.update(r);
+
+        assertEquals(mModel.getItemCount(), mAdapter.getItemCount());
+    }
+
+    // Tests that the item count is correct for a directory containing only files.
+    public void testItemCount_allFiles() {
+        mModel.update(NAMES);
+        assertEquals(mModel.getItemCount(), mAdapter.getItemCount());
+    }
+
+    // Tests that the item count is correct for a directory containing files and subdirs.
+    public void testItemCount_mixed() {
+        MatrixCursor c = new MatrixCursor(TestModel.COLUMNS);
+
+        for (int i = 0; i < 5; ++i) {
+            MatrixCursor.RowBuilder row = c.newRow();
+            row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY);
+            row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
+            row.add(Document.COLUMN_SIZE, i);
+            String mimeType =(i < 2) ? Document.MIME_TYPE_DIR : "text/*";
+            row.add(Document.COLUMN_MIME_TYPE, mimeType);
+        }
+        DirectoryResult r = new DirectoryResult();
+        r.cursor = c;
+        r.sortOrder = State.SORT_ORDER_SIZE;
+        mModel.update(r);
+
+        assertEquals(mModel.getItemCount() + 1, mAdapter.getItemCount());
+    }
+
+    private final class TestEnvironment implements DocumentsAdapter.Environment {
+        private final Context testContext;
+
+        private TestEnvironment(Context testContext) {
+            this.testContext = testContext;
+        }
+
+        @Override
+        public boolean isSelected(String id) {
+            return false;
+        }
+
+        @Override
+        public boolean isDocumentEnabled(String mimeType, int flags) {
+            return true;
+        }
+
+        @Override
+        public void initDocumentHolder(DocumentHolder holder) {}
+
+        @Override
+        public Model getModel() {
+            return mModel;
+        }
+
+        @Override
+        public State getDisplayState() {
+            return null;
+        }
+
+        @Override
+        public Context getContext() {
+            return testContext;
+        }
+
+        @Override
+        public int getColumnCount() {
+            return 4;
+        }
+
+        @Override
+        public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) {}
+    }
+
+    private static class DummyListener implements Model.UpdateListener {
+        public void onModelUpdate(Model model) {}
+        public void onModelUpdateFailed(Exception e) {}
+    }
+
+    private static class DummyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        public int getItemCount() { return 0; }
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return null;
+        }
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContentProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContentProvider.java
new file mode 100644
index 0000000..c8d424f
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContentProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.test.mock.MockContentProvider;
+
+import com.android.documentsui.model.DocumentInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A very simple test double for ContentProvider. Useful in this package only.
+ */
+class TestContentProvider extends MockContentProvider {
+    List<Uri> mDeleted = new ArrayList<>();
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        // Intercept and log delete method calls.
+        if (DocumentsContract.METHOD_DELETE_DOCUMENT.equals(method)) {
+            final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
+            mDeleted.add(documentUri);
+            return new Bundle();
+        } else {
+            return super.call(method, arg, extras);
+        }
+    }
+
+    public void assertWasDeleted(DocumentInfo doc) {
+        ModelTest.assertTrue(mDeleted.contains(doc.derivedUri));
+    }
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContext.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContext.java
new file mode 100644
index 0000000..714062d
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestContext.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.test.mock.MockContentResolver;
+
+public final class TestContext {
+
+    /**
+     * Returns a Context configured with test provider for authority.
+     */
+    static Context createStorageTestContext(Context context, String authority) {
+        final MockContentResolver testResolver = new MockContentResolver();
+        TestContentProvider provider = new TestContentProvider();
+        testResolver.addProvider(authority, provider);
+
+        return new ContextWrapper(context) {
+            @Override
+            public ContentResolver getContentResolver() {
+                return testResolver;
+            }
+        };
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java
new file mode 100644
index 0000000..267f47d
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestDocumentsAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import android.util.SparseArray;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A skeletal {@link DocumentsAdapter} test double.
+ */
+public class TestDocumentsAdapter extends DocumentsAdapter {
+
+    List<String> mModelIds = new ArrayList<>();
+
+    public TestDocumentsAdapter(List<String> modelIds) {
+        mModelIds = modelIds;
+    }
+
+    @Override
+    public void onModelUpdate(Model model) {
+    }
+
+    @Override
+    public void onModelUpdateFailed(Exception e) {
+    }
+
+    @Override
+    List<String> getModelIds() {
+        return mModelIds;
+    }
+
+    @Override
+    void onItemSelectionChanged(String id) {
+    }
+
+    @Override
+    String getModelId(int position) {
+        return mModelIds.get(position);
+    }
+
+    @Override
+    public SparseArray<String> hide(String... ids) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    void unhide(SparseArray<String> ids) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void onBindViewHolder(DocumentHolder holder, int position) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getItemCount() {
+        return mModelIds.size();
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java
new file mode 100644
index 0000000..f9cd3b2
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 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.documentsui.dirlist;
+
+import android.content.Context;
+import android.database.MatrixCursor;
+import android.provider.DocumentsContract.Document;
+
+import com.android.documentsui.DirectoryResult;
+import com.android.documentsui.RootCursorWrapper;
+import com.android.documentsui.dirlist.MultiSelectManager.Selection;
+
+import java.util.Random;
+import java.util.Set;
+
+public class TestModel extends Model {
+
+    static final String[] COLUMNS = new String[]{
+        RootCursorWrapper.COLUMN_AUTHORITY,
+        Document.COLUMN_DOCUMENT_ID,
+        Document.COLUMN_FLAGS,
+        Document.COLUMN_DISPLAY_NAME,
+        Document.COLUMN_SIZE,
+        Document.COLUMN_MIME_TYPE
+    };
+
+    private final String mAuthority;
+    private Set<String> mDeleted;
+
+    /**
+     * Creates a new context. context must be configured with provider for authority.
+     * @see TestContext#createStorageTestContext(Context, String).
+     */
+    public TestModel(Context context, String authority) {
+        super(context);
+        mAuthority = authority;
+    }
+
+    void update(String... names) {
+        Random rand = new Random();
+
+        MatrixCursor c = new MatrixCursor(COLUMNS);
+        for (int i = 0; i < names.length; i++) {
+            MatrixCursor.RowBuilder row = c.newRow();
+            row.add(RootCursorWrapper.COLUMN_AUTHORITY, mAuthority);
+            row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
+            row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_DELETE);
+            // Generate random document names and sizes. This forces the model's internal sort code
+            // to actually do something.
+            row.add(Document.COLUMN_DISPLAY_NAME, names[i]);
+            row.add(Document.COLUMN_SIZE, rand.nextInt());
+        }
+
+        DirectoryResult r = new DirectoryResult();
+        r.cursor = c;
+        update(r);
+    }
+
+    // Note that model id includes authority qualifier and is distinct
+    // WRT documentId because of this.
+    String idForPosition(int p) {
+        return createModelId(mAuthority, Integer.toString(p));
+    }
+
+    @Override
+    public void delete(Selection selected, DeletionListener listener) {
+        for (String id : selected.getAll()) {
+            mDeleted.add(id);
+        }
+        listener.onCompletion();
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java
index fc85f2b..c4cfd3a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView.AdapterDataObserver;
 import android.support.v7.widget.RecyclerView.OnScrollListener;
 import android.view.View;
 
@@ -28,10 +27,7 @@
 
 public class TestSelectionEnvironment implements SelectionEnvironment {
 
-    private List<String> mItems;
-
     public TestSelectionEnvironment(List<String> items) {
-        mItems = items;
     }
 
     @Override
@@ -114,27 +110,4 @@
     @Override
     public void focusItem(int position) {
     }
-
-    @Override
-    public String getModelIdFromAdapterPosition(int position) {
-        return mItems.get(position);
-    }
-
-    @Override
-    public int getItemCount() {
-        return mItems.size();
-    }
-
-    @Override
-    public List<String> getModelIds() {
-        return null;
-    }
-
-    @Override
-    public void notifyItemChanged(String id) {
-    }
-
-    @Override
-    public void registerDataObserver(AdapterDataObserver observer) {
-    }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java
similarity index 92%
rename from packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
rename to packages/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java
index 24a8113..35aad60 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/FileOperationServiceTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.documentsui;
+package com.android.documentsui.services;
 
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -34,6 +34,9 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
+import com.android.documentsui.DocumentsProviderHelper;
+import com.android.documentsui.Shared;
+import com.android.documentsui.StubProvider;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.RootInfo;
@@ -54,10 +57,10 @@
 import java.util.concurrent.TimeoutException;
 
 @MediumTest
-public class CopyServiceTest extends ServiceTestCase<CopyService> {
+public class FileOperationServiceTest extends ServiceTestCase<FileOperationService> {
 
-    public CopyServiceTest() {
-        super(CopyService.class);
+    public FileOperationServiceTest() {
+        super(FileOperationService.class);
     }
 
     private static String AUTHORITY = "com.android.documentsui.stubprovider";
@@ -139,7 +142,9 @@
                 testContent.getBytes());
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
-        moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+        moveIntent.putExtra(
+                FileOperationService.EXTRA_OPERATION,
+                FileOperationService.OPERATION_MOVE);
         startService(moveIntent);
 
         // 3 operations: file creation, writing data, deleting original.
@@ -235,7 +240,7 @@
         Uri testDir = createTestDirectory(srcPath);
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
-        moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+        moveIntent.putExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_MOVE);
         startService(moveIntent);
 
         // 2 operations: Directory creation, and removal of the original.
@@ -270,7 +275,7 @@
         mStorage.createRegularFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
-        moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+        moveIntent.putExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_MOVE);
         startService(moveIntent);
 
         // dir creation, then creation and writing of 3 files, then removal of src dir and 3 src
@@ -311,10 +316,8 @@
 
     public void testCopyVirtualNonTypedFile() throws Exception {
         String srcPath = "/non-typed.sth";
-        // Empty stream types causes the FLAG_SUPPORTS_TYPED_DOCUMENT to be not set.
-        ArrayList<String> streamTypes = new ArrayList<>();
         Uri testFile = mStorage.createVirtualFile(SRC_ROOT, srcPath, "virtual/mime-type",
-                streamTypes, "I love Tokyo!".getBytes());
+                null /* streamTypes */, "I love Tokyo!".getBytes());
 
         Intent intent = createCopyIntent(Lists.newArrayList(testFile));
         startService(intent);
@@ -334,7 +337,7 @@
         mStorage.simulateReadErrorsForFile(testFile);
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
-        moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+        moveIntent.putExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_MOVE);
         startService(moveIntent);
 
         try {
@@ -376,7 +379,7 @@
         mStorage.simulateReadErrorsForFile(errFile);
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
-        moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
+        moveIntent.putExtra(FileOperationService.EXTRA_OPERATION, FileOperationService.OPERATION_MOVE);
         startService(moveIntent);
 
         // - dst dir creation,
@@ -424,8 +427,14 @@
 
         DocumentStack stack = new DocumentStack();
         stack.push(DocumentInfo.fromUri(mResolver, dst));
-        final Intent copyIntent = new Intent(mContext, CopyService.class);
-        copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
+        final Intent copyIntent = new Intent(mContext, FileOperationService.class);
+        copyIntent.putExtra(
+                FileOperationService.EXTRA_OPERATION,
+                FileOperationService.OPERATION_COPY);
+        copyIntent.putExtra(
+                FileOperationService.EXTRA_JOB_ID,
+                FileOperationService.createJobId());
+        copyIntent.putParcelableArrayListExtra(FileOperationService.EXTRA_SRC_LIST, srcDocs);
         copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         return copyIntent;
@@ -511,7 +520,7 @@
         mResolver.addProvider(AUTHORITY, mStorage);
     }
 
-    private final class CopyJobListener implements CopyService.TestOnlyListener {
+    private final class CopyJobListener implements FileOperationService.TestOnlyListener {
 
         final CountDownLatch latch = new CountDownLatch(1);
         final List<DocumentInfo> failedDocs = new ArrayList<>();
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index dea5fa0..746457f 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -308,31 +308,31 @@
     <string name="airplane_mode">Airplane mode</string>
 
     <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_restart_pattern">Pattern required when you restart device.</string>
+    <string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
 
     <!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_restart_pin">PIN required when you restart device.</string>
+    <string name="kg_prompt_reason_restart_pin">PIN required after device restarts</string>
 
     <!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_restart_password">Password required when you restart device.</string>
+    <string name="kg_prompt_reason_restart_password">Password required after device restarts</string>
 
     <!-- An explanation text that the pattern needs to be solved since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security.</string>
+    <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security</string>
 
     <!-- An explanation text that the pin needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_timeout_pin">PIN required for additional security.</string>
+    <string name="kg_prompt_reason_timeout_pin">PIN required for additional security</string>
 
     <!-- An explanation text that the password needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_timeout_password">Password required for additional security.</string>
+    <string name="kg_prompt_reason_timeout_password">Password required for additional security</string>
 
     <!-- An explanation text that the pattern needs to be solved since profiles have just been switched. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles.</string>
+    <string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles</string>
 
     <!-- An explanation text that the pin needs to be entered since profiles have just been switched. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_switch_profiles_pin">PIN required when you switch profiles.</string>
+    <string name="kg_prompt_reason_switch_profiles_pin">PIN required when you switch profiles</string>
 
     <!-- An explanation text that the password needs to be entered since profiles have just been switched. [CHAR LIMIT=80] -->
-    <string name="kg_prompt_reason_switch_profiles_password">Password required when you switch profiles.</string>
+    <string name="kg_prompt_reason_switch_profiles_password">Password required when you switch profiles</string>
 
     <!-- An explanation text that the pattern needs to be solved since it hasn't been solved in a while. [CHAR LIMIT=80]-->
     <plurals name="kg_prompt_reason_time_pattern">
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index b03871a..64b443b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -123,39 +123,45 @@
             mPendingLockCheck.cancel(false);
         }
 
+        final int userId = KeyguardUpdateMonitor.getCurrentUser();
         if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
             // to avoid accidental lockout, only count attempts that are long enough to be a
             // real password. This may require some tweaking.
             setPasswordEntryInputEnabled(true);
-            onPasswordChecked(false /* matched */, 0, false /* not valid - too short */);
+            onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
             return;
         }
 
         mPendingLockCheck = LockPatternChecker.checkPassword(
                 mLockPatternUtils,
                 entry,
-                KeyguardUpdateMonitor.getCurrentUser(),
+                userId,
                 new LockPatternChecker.OnCheckCallback() {
                     @Override
                     public void onChecked(boolean matched, int timeoutMs) {
                         setPasswordEntryInputEnabled(true);
                         mPendingLockCheck = null;
-                        onPasswordChecked(matched, timeoutMs, true /* isValidPassword */);
+                        onPasswordChecked(userId, matched, timeoutMs,
+                                true /* isValidPassword */);
                     }
                 });
     }
 
-    private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) {
+    private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
+            boolean isValidPassword) {
+        boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
         if (matched) {
-            mDismissing = true;
-            mCallback.reportUnlockAttempt(true, 0);
-            mCallback.dismiss(true);
+            mCallback.reportUnlockAttempt(userId, true, 0);
+            if (dismissKeyguard) {
+                mDismissing = true;
+                mCallback.dismiss(true);
+            }
         } else {
             if (isValidPassword) {
-                mCallback.reportUnlockAttempt(false, timeoutMs);
+                mCallback.reportUnlockAttempt(userId, false, timeoutMs);
                 if (timeoutMs > 0) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
-                            KeyguardUpdateMonitor.getCurrentUser(), timeoutMs);
+                            userId, timeoutMs);
                     handleAttemptLockout(deadline);
                 }
             }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 29b319f..2a2f5a0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -227,22 +227,23 @@
                 mPendingLockCheck.cancel(false);
             }
 
+            final int userId = KeyguardUpdateMonitor.getCurrentUser();
             if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
                 mLockPatternView.enableInput();
-                onPatternChecked(false, 0, false /* not valid - too short */);
+                onPatternChecked(userId, false, 0, false /* not valid - too short */);
                 return;
             }
 
             mPendingLockCheck = LockPatternChecker.checkPattern(
                     mLockPatternUtils,
                     pattern,
-                    KeyguardUpdateMonitor.getCurrentUser(),
+                    userId,
                     new LockPatternChecker.OnCheckCallback() {
                         @Override
                         public void onChecked(boolean matched, int timeoutMs) {
                             mLockPatternView.enableInput();
                             mPendingLockCheck = null;
-                            onPatternChecked(matched, timeoutMs, true);
+                            onPatternChecked(userId, matched, timeoutMs, true);
                         }
                     });
             if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
@@ -250,18 +251,22 @@
             }
         }
 
-        private void onPatternChecked(boolean matched, int timeoutMs, boolean isValidPattern) {
+        private void onPatternChecked(int userId, boolean matched, int timeoutMs,
+                boolean isValidPattern) {
+            boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
             if (matched) {
-                mCallback.reportUnlockAttempt(true, 0);
-                mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
-                mCallback.dismiss(true);
+                mCallback.reportUnlockAttempt(userId, true, 0);
+                if (dismissKeyguard) {
+                    mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+                    mCallback.dismiss(true);
+                }
             } else {
                 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
                 if (isValidPattern) {
-                    mCallback.reportUnlockAttempt(false, timeoutMs);
+                    mCallback.reportUnlockAttempt(userId, false, timeoutMs);
                     if (timeoutMs > 0) {
                         long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
-                                KeyguardUpdateMonitor.getCurrentUser(), timeoutMs);
+                                userId, timeoutMs);
                         handleAttemptLockout(deadline);
                     }
                 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
index 836c195..232d4d2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -36,11 +36,12 @@
 
     /**
      * Call to report an unlock attempt.
+     * @param userId id of the user whose unlock attempt is recorded.
      * @param success set to 'true' if user correctly entered security credentials.
      * @param timeoutMs timeout in milliseconds to wait before reattempting an unlock.
      *                  Only nonzero if 'success' is false
      */
-    void reportUnlockAttempt(boolean success, int timeoutMs);
+    void reportUnlockAttempt(int userId, boolean success, int timeoutMs);
 
     /**
      * Resets the keyguard view.
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index f45b9bd..409f6a7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -205,7 +205,8 @@
 
         if (messageId != 0) {
             final String message = mContext.getString(messageId,
-                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
+                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(
+                            KeyguardUpdateMonitor.getCurrentUser()),
                     timeoutInSeconds);
             showDialog(null, message);
         }
@@ -249,16 +250,15 @@
         showDialog(null, message);
     }
 
-    private void reportFailedUnlockAttempt(int timeoutMs) {
+    private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
         final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
-        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
+        final int failedAttempts = monitor.getFailedUnlockAttempts(userId) + 1; // +1 for this time
 
         if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
 
-        final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
         final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
         final int failedAttemptsBeforeWipe =
-                dpm.getMaximumFailedPasswordsForWipe(null, currentUser);
+                dpm.getMaximumFailedPasswordsForWipe(null, userId);
 
         final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
                 (failedAttemptsBeforeWipe - failedAttempts)
@@ -268,9 +268,9 @@
             // N attempts. Once we get below the grace period, we post this dialog every time as a
             // clear warning until the deletion fires.
             // Check which profile has the strictest policy for failed password attempts
-            final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(currentUser);
+            final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
             int userType = USER_TYPE_PRIMARY;
-            if (expiringUser == currentUser) {
+            if (expiringUser == userId) {
                 // TODO: http://b/23522538
                 if (expiringUser != UserHandle.USER_SYSTEM) {
                     userType = USER_TYPE_SECONDARY_USER;
@@ -286,8 +286,8 @@
                 showWipeDialog(failedAttempts, userType);
             }
         }
-        monitor.reportFailedStrongAuthUnlockAttempt();
-        mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
+        monitor.reportFailedStrongAuthUnlockAttempt(userId);
+        mLockPatternUtils.reportFailedPasswordAttempt(userId);
         if (timeoutMs > 0) {
             showTimeoutDialog(timeoutMs);
         }
@@ -422,14 +422,13 @@
             return mIsVerifyUnlockOnly;
         }
 
-        public void reportUnlockAttempt(boolean success, int timeoutMs) {
+        public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
             KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
             if (success) {
                 monitor.clearFailedUnlockAttempts();
-                mLockPatternUtils.reportSuccessfulPasswordAttempt(
-                        KeyguardUpdateMonitor.getCurrentUser());
+                mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
             } else {
-                KeyguardSecurityContainer.this.reportFailedUnlockAttempt(timeoutMs);
+                KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
             }
         }
 
@@ -445,7 +444,7 @@
         @Override
         public void userActivity() { }
         @Override
-        public void reportUnlockAttempt(boolean success, int timeoutMs) { }
+        public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
         @Override
         public boolean isVerifyUnlockOnly() { return false; }
         @Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8102c34..b4f9b9f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1552,12 +1552,12 @@
         mFailedAttempts.delete(sCurrentUser);
     }
 
-    public int getFailedUnlockAttempts() {
-        return mFailedAttempts.get(sCurrentUser, 0);
+    public int getFailedUnlockAttempts(int userId) {
+        return mFailedAttempts.get(userId, 0);
     }
 
-    public void reportFailedStrongAuthUnlockAttempt() {
-        mFailedAttempts.put(sCurrentUser, getFailedUnlockAttempts() + 1);
+    public void reportFailedStrongAuthUnlockAttempt(int userId) {
+        mFailedAttempts.put(userId, getFailedUnlockAttempts(userId) + 1);
     }
 
     public void clearFingerprintRecognized() {
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index f592a1f..cde28fc 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -30,31 +30,37 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "nativehelper/ScopedPrimitiveArray.h"
 
 namespace {
 
-constexpr int min(int a, int b) {
-    return a < b ? a : b;
-}
-
-// Maximum number of bytes to write in one request.
+// The numbers came from sdcard.c.
+// Maximum number of bytes to write/read in one request/one reply.
 constexpr size_t MAX_WRITE = 256 * 1024;
+constexpr size_t MAX_READ = 128 * 1024;
+
+constexpr size_t NUM_MAX_HANDLES = 1024;
 
 // Largest possible request.
 // The request size is bounded by the maximum size of a FUSE_WRITE request
 // because it has the largest possible data payload.
 constexpr size_t MAX_REQUEST_SIZE = sizeof(struct fuse_in_header) +
-        sizeof(struct fuse_write_in) + MAX_WRITE;
+        sizeof(struct fuse_write_in) + (MAX_WRITE > MAX_READ ? MAX_WRITE : MAX_READ);
 
 static jclass app_fuse_class;
+static jmethodID app_fuse_get_file_size;
+static jmethodID app_fuse_get_object_bytes;
 
+// NOTE:
+// FuseRequest and FuseResponse shares the same buffer to save memory usage, so the handlers must
+// not access input buffer after writing data to output buffer.
 struct FuseRequest {
     char buffer[MAX_REQUEST_SIZE];
     FuseRequest() {}
     const struct fuse_in_header& header() const {
         return *(const struct fuse_in_header*) buffer;
     }
-    const void* data() const {
+    void* data() {
         return (buffer + sizeof(struct fuse_in_header));
     }
     size_t data_length() const {
@@ -62,6 +68,26 @@
     }
 };
 
+template<typename T>
+class FuseResponse {
+   size_t size_;
+   T* const buffer_;
+public:
+   FuseResponse(void* buffer) : size_(0), buffer_(static_cast<T*>(buffer)) {}
+
+   void prepare_buffer(size_t size = sizeof(T)) {
+       memset(buffer_, 0, size);
+       size_ = size;
+   }
+
+   void set_size(size_t size) {
+       size_ = size;
+   }
+
+   size_t size() const { return size_; }
+   T* data() const { return buffer_; }
+};
+
 class ScopedFd {
     int mFd;
 
@@ -76,18 +102,28 @@
 };
 
 /**
- * The class is used to access AppFuse class in Java from fuse handlers.
+ * Fuse implementation consists of handlers parsing FUSE commands.
  */
 class AppFuse {
-public:
-    AppFuse(JNIEnv* /*env*/, jobject /*self*/) {
-    }
+    JNIEnv* env_;
+    jobject self_;
 
-    bool handle_fuse_request(int fd, const FuseRequest& req) {
-        ALOGV("Request op=%d", req.header().opcode);
-        switch (req.header().opcode) {
+    // Map between file handle and inode.
+    std::map<uint32_t, uint64_t> handles_;
+    uint32_t handle_counter_;
+
+public:
+    AppFuse(JNIEnv* env, jobject self) :
+        env_(env), self_(self), handle_counter_(0) {}
+
+    bool handle_fuse_request(int fd, FuseRequest* req) {
+        ALOGV("Request op=%d", req->header().opcode);
+        switch (req->header().opcode) {
             // TODO: Handle more operations that are enough to provide seekable
             // FD.
+            case FUSE_LOOKUP:
+                invoke_handler(fd, req, &AppFuse::handle_fuse_lookup);
+                return true;
             case FUSE_INIT:
                 invoke_handler(fd, req, &AppFuse::handle_fuse_init);
                 return true;
@@ -96,22 +132,60 @@
                 return true;
             case FUSE_FORGET:
                 return false;
+            case FUSE_OPEN:
+                invoke_handler(fd, req, &AppFuse::handle_fuse_open);
+                return true;
+            case FUSE_READ:
+                invoke_handler(fd, req, &AppFuse::handle_fuse_read);
+                return true;
+            case FUSE_RELEASE:
+                invoke_handler(fd, req, &AppFuse::handle_fuse_release);
+                return true;
+            case FUSE_FLUSH:
+                invoke_handler(fd, req, &AppFuse::handle_fuse_flush);
+                return true;
             default: {
                 ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n",
-                      req.header().opcode,
-                      req.header().unique,
-                      req.header().nodeid);
-                fuse_reply(fd, req.header().unique, -ENOSYS, NULL, 0);
+                      req->header().opcode,
+                      req->header().unique,
+                      req->header().nodeid);
+                fuse_reply(fd, req->header().unique, -ENOSYS, NULL, 0);
                 return true;
             }
         }
     }
 
 private:
+    int handle_fuse_lookup(const fuse_in_header& header,
+                           const char* name,
+                           FuseResponse<fuse_entry_out>* out) {
+        if (header.nodeid != 1) {
+            return -ENOENT;
+        }
+
+        const int n = atoi(name);
+        if (n == 0) {
+            return -ENOENT;
+        }
+
+        int64_t size = get_file_size(n);
+        if (size < 0) {
+            return -ENOENT;
+        }
+
+        out->prepare_buffer();
+        out->data()->nodeid = n;
+        out->data()->attr_valid = 10;
+        out->data()->entry_valid = 10;
+        out->data()->attr.ino = n;
+        out->data()->attr.mode = S_IFREG | 0777;
+        out->data()->attr.size = size;
+        return 0;
+    }
+
     int handle_fuse_init(const fuse_in_header&,
                          const fuse_init_in* in,
-                         fuse_init_out* out,
-                         size_t* reply_size) {
+                         FuseResponse<fuse_init_out>* out) {
         // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
         // defined (fuse version 7.6). The structure is the same from 7.6 through
         // 7.22. Beginning with 7.23, the structure increased in size and added
@@ -123,67 +197,159 @@
             return -1;
         }
 
+        // Before writing |out|, we need to copy data from |in|.
+        const uint32_t minor = in->minor;
+        const uint32_t max_readahead = in->max_readahead;
+
         // We limit ourselves to 15 because we don't handle BATCH_FORGET yet
-        out->minor = min(in->minor, 15);
+        size_t response_size = sizeof(fuse_init_out);
 #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
         // FUSE_KERNEL_VERSION >= 23.
 
         // If the kernel only works on minor revs older than or equal to 22,
         // then use the older structure size since this code only uses the 7.22
         // version of the structure.
-        if (in->minor <= 22) {
-            *reply_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+        if (minor <= 22) {
+            response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
         }
-#else
-        // Don't drop this line to prevent an 'unused' compile error.
-        *reply_size = sizeof(fuse_init_out);
 #endif
-
-        out->major = FUSE_KERNEL_VERSION;
-        out->max_readahead = in->max_readahead;
-        out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
-        out->max_background = 32;
-        out->congestion_threshold = 32;
-        out->max_write = MAX_WRITE;
+        out->prepare_buffer(response_size);
+        out->data()->major = FUSE_KERNEL_VERSION;
+        out->data()->minor = std::min(minor, 15u);
+        out->data()->max_readahead = max_readahead;
+        out->data()->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+        out->data()->max_background = 32;
+        out->data()->congestion_threshold = 32;
+        out->data()->max_write = MAX_WRITE;
 
         return 0;
     }
 
     int handle_fuse_getattr(const fuse_in_header& header,
                             const fuse_getattr_in* /* in */,
-                            fuse_attr_out* out,
-                            size_t* /*unused*/) {
-        if (header.nodeid != 1) {
-            return -ENOENT;
+                            FuseResponse<fuse_attr_out>* out) {
+        out->prepare_buffer();
+        out->data()->attr_valid = 10;
+        out->data()->attr.ino = header.nodeid;
+        if (header.nodeid == 1) {
+            out->data()->attr.mode = S_IFDIR | 0777;
+            out->data()->attr.size = 0;
+        } else {
+            int64_t size = get_file_size(header.nodeid);
+            if (size < 0) {
+                return -ENOENT;
+            }
+            out->data()->attr.mode = S_IFREG | 0777;
+            out->data()->attr.size = size;
         }
-        out->attr_valid = 1000 * 60 * 10;
-        out->attr.ino = header.nodeid;
-        out->attr.mode = S_IFDIR | 0777;
-        out->attr.size = 0;
+
+        return 0;
+    }
+
+    int handle_fuse_open(const fuse_in_header& header,
+                         const fuse_open_in* /* in */,
+                         FuseResponse<fuse_open_out>* out) {
+        if (handles_.size() >= NUM_MAX_HANDLES) {
+            // Too many open files.
+            return -EMFILE;
+        }
+        uint32_t handle;
+        do {
+           handle = handle_counter_++;
+        } while (handles_.count(handle) != 0);
+        handles_.insert(std::make_pair(handle, header.nodeid));
+
+        out->prepare_buffer();
+        out->data()->fh = handle;
+        return 0;
+    }
+
+    int handle_fuse_read(const fuse_in_header& /* header */,
+                         const fuse_read_in* in,
+                         FuseResponse<void>* out) {
+        if (in->size > MAX_READ) {
+            return -EINVAL;
+        }
+        const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
+        if (it == handles_.end()) {
+            return -EBADF;
+        }
+        uint64_t offset = in->offset;
+        uint32_t size = in->size;
+
+        // Overwrite the size after writing data.
+        out->prepare_buffer(0);
+        const int64_t result = get_object_bytes(it->second, offset, size, out->data());
+        if (result < 0) {
+            return -EIO;
+        }
+        out->set_size(result);
+        return 0;
+    }
+
+    int handle_fuse_release(const fuse_in_header& /* header */,
+                            const fuse_release_in* in,
+                            FuseResponse<void>* /* out */) {
+        handles_.erase(in->fh);
+        return 0;
+    }
+
+    int handle_fuse_flush(const fuse_in_header& /* header */,
+                          const void* /* in */,
+                          FuseResponse<void>* /* out */) {
         return 0;
     }
 
     template <typename T, typename S>
     void invoke_handler(int fd,
-                        const FuseRequest& request,
+                        FuseRequest* request,
                         int (AppFuse::*handler)(const fuse_in_header&,
                                                 const T*,
-                                                S*,
-                                                size_t*),
-                        size_t reply_size = sizeof(S)) {
-        char reply_data[reply_size];
-        memset(reply_data, 0, reply_size);
+                                                FuseResponse<S>*)) {
+        FuseResponse<S> response(request->data());
         const int reply_code = (this->*handler)(
-                request.header(),
-                static_cast<const T*>(request.data()),
-                reinterpret_cast<S*>(reply_data),
-                &reply_size);
+                request->header(),
+                static_cast<const T*>(request->data()),
+                &response);
         fuse_reply(
                 fd,
-                request.header().unique,
+                request->header().unique,
                 reply_code,
-                reply_data,
-                reply_size);
+                request->data(),
+                response.size());
+    }
+
+    int64_t get_file_size(int inode) {
+        return static_cast<int64_t>(env_->CallLongMethod(
+                self_,
+                app_fuse_get_file_size,
+                static_cast<int>(inode)));
+    }
+
+    int64_t get_object_bytes(
+            int inode,
+            uint64_t offset,
+            uint32_t size,
+            void* buf) {
+        const uint32_t read_size = static_cast<uint32_t>(std::min(
+                static_cast<uint64_t>(size),
+                get_file_size(inode) - offset));
+        const jbyteArray array = (jbyteArray) env_->CallObjectMethod(
+                self_,
+                app_fuse_get_object_bytes,
+                inode,
+                offset,
+                read_size);
+        if (array == nullptr) {
+            return -1;
+        }
+        ScopedByteArrayRO bytes(env_, array);
+        if (bytes.size() != read_size || bytes.get() == nullptr) {
+            return -1;
+        }
+
+        memcpy(buf, bytes.get(), read_size);
+        return read_size;
     }
 
     static void fuse_reply(int fd, int unique, int reply_code, void* reply_data,
@@ -219,6 +385,7 @@
     ALOGD("Start fuse loop.");
     while (true) {
         FuseRequest request;
+
         const ssize_t result = TEMP_FAILURE_RETRY(
                 read(fd, request.buffer, sizeof(request.buffer)));
         if (result < 0) {
@@ -242,7 +409,7 @@
             continue;
         }
 
-        if (!appfuse.handle_fuse_request(fd, request)) {
+        if (!appfuse.handle_fuse_request(fd, &request)) {
             return JNI_TRUE;
         }
     }
@@ -272,12 +439,27 @@
         ALOGE("Can't find com/android/mtp/AppFuse");
         return -1;
     }
+
     app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz));
     if (app_fuse_class == nullptr) {
         ALOGE("Can't obtain global reference for com/android/mtp/AppFuse");
         return -1;
     }
 
+    app_fuse_get_file_size = env->GetMethodID(
+            app_fuse_class, "getFileSize", "(I)J");
+    if (app_fuse_get_file_size == nullptr) {
+        ALOGE("Can't find getFileSize");
+        return -1;
+    }
+
+    app_fuse_get_object_bytes = env->GetMethodID(
+            app_fuse_class, "getObjectBytes", "(IJI)[B");
+    if (app_fuse_get_object_bytes == nullptr) {
+        ALOGE("Can't find getObjectBytes");
+        return -1;
+    }
+
     const int result = android::AndroidRuntime::registerNativeMethods(
             env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods));
     if (result < 0) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 2c09ad1..5ffd7cf 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -17,16 +17,14 @@
 package com.android.mtp;
 
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.storage.StorageManager;
 import android.util.Log;
-
 import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 
-import android.os.Process;
-
 /**
  * TODO: Remove VisibleForTesting class.
  */
@@ -37,12 +35,14 @@
     }
 
     private final String mName;
+    private final Callback mCallback;
     private final Thread mMessageThread;
     private ParcelFileDescriptor mDeviceFd;
 
     @VisibleForTesting
-    AppFuse(String name) {
+    AppFuse(String name, Callback callback) {
         mName = name;
+        mCallback = callback;
         mMessageThread = new Thread(new Runnable() {
             @Override
             public void run() {
@@ -72,10 +72,45 @@
         }
     }
 
+    /**
+     * @param i
+     * @throws FileNotFoundException
+     */
+    @VisibleForTesting
+    public ParcelFileDescriptor openFile(int i) throws FileNotFoundException {
+        return ParcelFileDescriptor.open(new File(
+                getMountPoint(),
+                Integer.toString(i)),
+                ParcelFileDescriptor.MODE_READ_ONLY);
+    }
+
     @VisibleForTesting
     File getMountPoint() {
         return new File("/mnt/appfuse/" + Process.myUid() + "_" + mName);
     }
 
+    static interface Callback {
+        long getFileSize(int inode) throws FileNotFoundException;
+        byte[] getObjectBytes(int inode, long offset, int size) throws IOException;
+    }
+
+    @VisibleForTesting
+    private long getFileSize(int inode) {
+        try {
+            return mCallback.getFileSize(inode);
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    @VisibleForTesting
+    private byte[] getObjectBytes(int inode, long offset, int size) {
+        try {
+            return mCallback.getObjectBytes(inode, offset, size);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
     private native boolean native_start_app_fuse_loop(int fd);
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
index 71df5c1..02d07b9 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -21,11 +21,14 @@
     public final String name;
     public final boolean opened;
     public final MtpRoot[] roots;
+    public final int[] operationsSupported;
 
-    MtpDeviceRecord(int deviceId, String name, boolean opened, MtpRoot[] roots) {
+    MtpDeviceRecord(
+            int deviceId, String name, boolean opened, MtpRoot[] roots, int[] operationsSupported) {
         this.deviceId = deviceId;
         this.name = name;
         this.opened = opened;
         this.roots = roots;
+        this.operationsSupported = operationsSupported;
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 88cab8b..0d81a30 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -24,6 +24,7 @@
 import android.hardware.usb.UsbManager;
 import android.mtp.MtpConstants;
 import android.mtp.MtpDevice;
+import android.mtp.MtpDeviceInfo;
 import android.mtp.MtpEvent;
 import android.mtp.MtpObjectInfo;
 import android.os.CancellationSignal;
@@ -123,9 +124,11 @@
             if (!isMtpDevice(device)) {
                 continue;
             }
-            final boolean opened = mDevices.get(device.getDeviceId()) != null;
+            final MtpDevice mtpDevice = mDevices.get(device.getDeviceId());
+            final boolean opened = mtpDevice != null;
             final String name = device.getProductName();
             MtpRoot[] roots;
+            int[] operationsSupported = null;
             if (opened) {
                 try {
                     roots = getRoots(device.getDeviceId());
@@ -136,10 +139,19 @@
                     // the device is physically connected.
                     roots = new MtpRoot[0];
                 }
+                final MtpDeviceInfo info = mtpDevice.getDeviceInfo();
+                if (info != null) {
+                    operationsSupported = mtpDevice.getDeviceInfo().getOperationsSupported();
+                }
+                if (operationsSupported == null) {
+                    operationsSupported = new int[0];
+                }
             } else {
                 roots = new MtpRoot[0];
+                operationsSupported = new int[0];
             }
-            devices.add(new MtpDeviceRecord(device.getDeviceId(), name, opened, roots));
+            devices.add(new MtpDeviceRecord(
+                    device.getDeviceId(), name, opened, roots, operationsSupported));
         }
         return devices.toArray(new MtpDeviceRecord[devices.size()]);
     }
@@ -176,6 +188,15 @@
         }
     }
 
+    @VisibleForTesting
+    long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer)
+            throws IOException {
+        final MtpDevice device = getDevice(deviceId);
+        synchronized (device) {
+            return device.getPartialObject(objectHandle, offset, size, buffer);
+        }
+    }
+
     byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
         final MtpDevice device = getDevice(deviceId);
         synchronized (device) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index c216c77..15b8ef3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -7,9 +7,6 @@
 import android.provider.DocumentsContract;
 import android.util.Log;
 
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.FutureTask;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
index b66d8eb..76bd2b5 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
@@ -16,24 +16,27 @@
 
 package com.android.mtp;
 
+import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
 
-@SmallTest
+/**
+ * TODO: Enable this test after adding SELinux policies for appfuse.
+ */
+@MediumTest
 public class AppFuseTest extends AndroidTestCase {
-    /**
-     * TODO: Enable this test after adding SELinux policies for appfuse.
-     * @throws ErrnoException
-     * @throws InterruptedException
-     */
-    public void disabled_testBasic() throws ErrnoException, InterruptedException {
+
+    public void disabled_testMount() throws ErrnoException, InterruptedException {
         final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
-        final AppFuse appFuse = new AppFuse("test");
+        final AppFuse appFuse = new AppFuse("test", new TestCallback());
         appFuse.mount(storageManager);
         final File file = appFuse.getMountPoint();
         assertTrue(file.isDirectory());
@@ -41,4 +44,86 @@
         appFuse.close();
         assertTrue(1 != Os.stat(file.getPath()).st_ino);
     }
+
+    public void disabled_testOpenFile() throws IOException {
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        final int INODE = 10;
+        final AppFuse appFuse = new AppFuse(
+                "test",
+                new TestCallback() {
+                    @Override
+                    public long getFileSize(int inode) throws FileNotFoundException {
+                        if (INODE == inode) {
+                            return 1024;
+                        }
+                        throw new FileNotFoundException();
+                    }
+                });
+        appFuse.mount(storageManager);
+        final ParcelFileDescriptor fd = appFuse.openFile(INODE);
+        fd.close();
+        appFuse.close();
+    }
+
+    public void disabled_testOpenFile_error() {
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        final int INODE = 10;
+        final AppFuse appFuse = new AppFuse("test", new TestCallback());
+        appFuse.mount(storageManager);
+        try {
+            appFuse.openFile(INODE);
+            fail();
+        } catch (Throwable t) {
+            assertTrue(t instanceof FileNotFoundException);
+        }
+        appFuse.close();
+    }
+
+    public void disabled_testReadFile() throws IOException {
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        final int INODE = 10;
+        final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+        final AppFuse appFuse = new AppFuse(
+                "test",
+                new TestCallback() {
+                    @Override
+                    public long getFileSize(int inode) throws FileNotFoundException {
+                        if (inode == INODE) {
+                            return BYTES.length;
+                        }
+                        return super.getFileSize(inode);
+                    }
+
+                    @Override
+                    public byte[] getObjectBytes(int inode, long offset, int size)
+                            throws IOException {
+                        if (inode == INODE) {
+                            return Arrays.copyOfRange(BYTES, (int) offset, (int) offset + size);
+                        }
+                        return super.getObjectBytes(inode, offset, size);
+                    }
+                });
+        appFuse.mount(storageManager);
+        final ParcelFileDescriptor fd = appFuse.openFile(INODE);
+        try (final ParcelFileDescriptor.AutoCloseInputStream stream =
+                new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+            final byte[] buffer = new byte[1024];
+            final int size = stream.read(buffer, 0, buffer.length);
+            assertEquals(5, size);
+        }
+        appFuse.close();
+    }
+
+    private static class TestCallback implements AppFuse.Callback {
+        @Override
+        public long getFileSize(int inode) throws FileNotFoundException {
+            throw new FileNotFoundException();
+        }
+
+        @Override
+        public byte[] getObjectBytes(int inode, long offset, int size)
+                throws IOException {
+            throw new IOException();
+        }
+    }
 }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 1e1ea0a..c39d5b3 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -77,7 +77,7 @@
     public void testPutSingleStorageDocuments() throws Exception {
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -425,9 +425,9 @@
         };
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device A", true, new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device A", true, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(1, "Device B", true, new MtpRoot[0]));
+                new MtpDeviceRecord(1, "Device B", true, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -562,7 +562,7 @@
 
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device",  false,  new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device",  false,  new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -640,7 +640,7 @@
     public void testReplaceExistingRoots() {
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         // The client code should be able to replace existing rows with new information.
@@ -691,7 +691,7 @@
         // Add one.
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device", true, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         mDatabase.getMapper().startAddingDocuments("1");
@@ -745,7 +745,7 @@
         // Add device document.
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(0, "Device", false, new MtpRoot[0]));
+                new MtpDeviceRecord(0, "Device", false, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
 
         // It the device does not have storages, it shows a device root.
@@ -895,7 +895,7 @@
     public void testGetDocumentIdForDevice() {
         mDatabase.getMapper().startAddingDocuments(null);
         mDatabase.getMapper().putDeviceDocument(
-                new MtpDeviceRecord(100, "Device", true, new MtpRoot[0]));
+                new MtpDeviceRecord(100, "Device", true, new MtpRoot[0], new int[0]));
         mDatabase.getMapper().stopAddingDocuments(null);
         assertEquals("1", mDatabase.getDocumentIdForDevice(100));
     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 71c4897..44841af 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -68,7 +68,8 @@
                             1024 /* free space */,
                             2048 /* total space */,
                             "" /* no volume identifier */)
-                }));
+                },
+                new int[0]));
 
         mProvider.openDevice(0);
         mResolver.waitForNotification(ROOTS_URI, 1);
@@ -107,7 +108,8 @@
                             1024 /* free space */,
                             2048 /* total space */,
                             "" /* no volume identifier */)
-                }));
+                },
+                new int[0]));
         mProvider.openDevice(0);
         mResolver.waitForNotification(ROOTS_URI, 1);
     }
@@ -127,7 +129,8 @@
                                 1024 /* free space */,
                                 2048 /* total space */,
                                 "" /* no volume identifier */)
-                }));
+                },
+                new int[0]));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
                 "Device",
@@ -141,7 +144,8 @@
                             2048 /* free space */,
                             4096 /* total space */,
                             "Identifier B" /* no volume identifier */)
-                }));
+                },
+                new int[0]));
 
         {
             mProvider.openDevice(0);
@@ -175,8 +179,8 @@
 
     public void testQueryRoots_error() throws Exception {
         setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
-        mMtpManager.addValidDevice(
-                new MtpDeviceRecord(0, "Device A", false /* unopened */, new MtpRoot[0]));
+        mMtpManager.addValidDevice(new MtpDeviceRecord(
+                0, "Device A", false /* unopened */, new MtpRoot[0], new int[0]));
         mMtpManager.addValidDevice(new MtpDeviceRecord(
                 1,
                 "Device",
@@ -190,7 +194,8 @@
                             2048 /* free space */,
                             4096 /* total space */,
                             "Identifier B" /* no volume identifier */)
-                }));
+                },
+                new int[0]));
         {
             mProvider.openDevice(0);
             mProvider.openDevice(1);
@@ -433,7 +438,7 @@
             throws InterruptedException, TimeoutException, IOException {
         final int changeCount = mResolver.getChangeCount(ROOTS_URI);
         mMtpManager.addValidDevice(
-                new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots));
+                new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots, new int[0]));
         mProvider.openDevice(deviceId);
         mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
         return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index 5e95e4f..25e9900 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -19,23 +19,27 @@
 import android.content.Context;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
+import android.mtp.MtpEvent;
+import android.mtp.MtpObjectInfo;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
 import android.test.InstrumentationTestCase;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.concurrent.Callable;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 
 @RealDeviceTest
 public class MtpManagerTest extends InstrumentationTestCase {
-
     private static final int TIMEOUT_MS = 1000;
     UsbManager mUsbManager;
     MtpManager mManager;
     UsbDevice mUsbDevice;
-    int mRequest;
 
     @Override
     public void setUp() throws Exception {
@@ -71,11 +75,77 @@
                 });
         final Thread thread = new Thread(future);
         thread.start();
-        Thread.sleep(TIMEOUT_MS);
+        SystemClock.sleep(TIMEOUT_MS);
         signal.cancel();
         assertTrue(future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    public void testOperationsSupported() {
+        final MtpDeviceRecord[] records = mManager.getDevices();
+        assertEquals(1, records.length);
+        assertNotNull(records[0].operationsSupported);
+        getInstrumentation().show(Arrays.toString(records[0].operationsSupported));
+    }
+
+    public void testEventObjectAdded() throws Exception {
+        while (true) {
+            getInstrumentation().show("Please take a photo by using connected MTP device.");
+            final CancellationSignal signal = new CancellationSignal();
+            MtpEvent event = mManager.readEvent(mUsbDevice.getDeviceId(), signal);
+            if (event.getEventCode() != MtpConstants.EVENT_OBJECT_ADDED) {
+                continue;
+            }
+            assertTrue(event.getObjectHandle() != 0);
+            break;
+        }
+    }
+
+    public void testCreateDocumentAndGetPartialObject() throws Exception {
+        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+        final ParcelFileDescriptor.AutoCloseOutputStream stream =
+                new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
+        int storageId = 0;
+        for (final MtpDeviceRecord record : mManager.getDevices()) {
+            if (record.deviceId == mUsbDevice.getDeviceId()) {
+                storageId = record.roots[0].mStorageId;
+                break;
+            }
+        }
+        assertTrue("Valid storage not found.", storageId != 0);
+        final String testFileName = "MtpManagerTest_testFile.txt";
+        for (final int handle : mManager.getObjectHandles(
+                mUsbDevice.getDeviceId(), storageId, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN)) {
+            if (mManager.getObjectInfo(mUsbDevice.getDeviceId(), handle)
+                    .getName().equals(testFileName)) {
+                mManager.deleteDocument(mUsbDevice.getDeviceId(), handle);
+                break;
+            }
+        }
+        final byte[] expectedBytes = "Hello Android!".getBytes("ascii");
+        final int objectHandle;
+        try {
+            stream.write(expectedBytes);
+            objectHandle = mManager.createDocument(
+                    mUsbDevice.getDeviceId(),
+                    new MtpObjectInfo.Builder()
+                            .setStorageId(storageId)
+                            .setName(testFileName)
+                            .setCompressedSize(expectedBytes.length)
+                            .setFormat(MtpConstants.FORMAT_TEXT)
+                            .build(),
+                    fds[0]);
+        } finally {
+            stream.close();
+        }
+        final byte[] bytes = new byte[100];
+        assertEquals(5, mManager.getPartialObject(
+                mUsbDevice.getDeviceId(), objectHandle, 0, 5, bytes));
+        assertEquals("Hello", new String(bytes, 0, 5, "ascii"));
+        assertEquals(8, mManager.getPartialObject(
+                mUsbDevice.getDeviceId(), objectHandle, 6, 100, bytes));
+        assertEquals("Android!", new String(bytes, 0, 8, "ascii"));
+    }
+
     private Context getContext() {
         return getInstrumentation().getContext();
     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index bbd0a30..3934b88 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -76,7 +76,7 @@
                 result[i] = device;
             } else {
                 result[i] = new MtpDeviceRecord(
-                        device.deviceId, device.name, device.opened, new MtpRoot[0]);
+                        device.deviceId, device.name, device.opened, new MtpRoot[0], new int[0]);
             }
         }
         return result;
@@ -90,7 +90,7 @@
         }
         mDevices.put(
                 deviceId,
-                new MtpDeviceRecord(device.deviceId, device.name, true, device.roots));
+                new MtpDeviceRecord(device.deviceId, device.name, true, device.roots, new int[0]));
     }
 
     @Override
@@ -101,7 +101,7 @@
         }
         mDevices.put(
                 deviceId,
-                new MtpDeviceRecord(device.deviceId, device.name, false, device.roots));
+                new MtpDeviceRecord(device.deviceId, device.name, false, device.roots, new int[0]));
     }
 
     @Override
@@ -203,4 +203,9 @@
         }
         return Arrays.copyOf(result, count);
     }
+
+    @Override
+    byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException {
+        return mImportFileBytes.get(pack(deviceId, objectHandle));
+    }
 }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
index 611e831..ffcc088 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -16,27 +16,21 @@
 
 package com.android.mtp;
 
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbManager;
+import android.os.SystemClock;
 
 import java.io.IOException;
 import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
+import java.util.Objects;
+
 import junit.framework.Assert;
 
 /**
  * Static utility methods for testing.
  */
 final class TestUtil {
-    private static final String ACTION_USB_PERMISSION =
-            "com.android.mtp.USB_PERMISSION";
-
     private TestUtil() {}
 
     /**
@@ -46,40 +40,34 @@
     static UsbDevice setupMtpDevice(
             TestResultInstrumentation instrumentation,
             UsbManager usbManager,
-            MtpManager manager) throws InterruptedException, IOException {
-        for (int i = 0; i < 2; i++) {
-            final UsbDevice device = findMtpDevice(instrumentation, usbManager);
-            manager.openDevice(device.getDeviceId());
+            MtpManager manager) {
+        while (true) {
             try {
+                final UsbDevice device = findMtpDevice(usbManager, manager);
                 waitForStorages(instrumentation, manager, device.getDeviceId());
                 return device;
             } catch (IOException exp) {
+                instrumentation.show(Objects.toString(exp.getMessage()));
+                SystemClock.sleep(1000);
                 // When the MTP device is Android, and it changes the USB device type from
                 // "Charging" to "MTP", the device ID will be updated. We need to find a device
                 // again.
                 continue;
             }
         }
-        throw new IOException("Failed to obtain MTP devices");
     }
 
     private static UsbDevice findMtpDevice(
-            TestResultInstrumentation instrumentation,
-            UsbManager usbManager) throws InterruptedException {
-        while (true) {
-            final HashMap<String,UsbDevice> devices = usbManager.getDeviceList();
-            if (devices.size() == 0) {
-                instrumentation.show("Wait for devices.");
-                Thread.sleep(1000);
-                continue;
-            }
-            final UsbDevice device = devices.values().iterator().next();
-            requestPermission(instrumentation, usbManager, device);
+            UsbManager usbManager,
+            MtpManager manager) throws IOException {
+        final HashMap<String,UsbDevice> devices = usbManager.getDeviceList();
+        if (devices.size() == 0) {
+            throw new IOException("Device not found.");
+        }
+        final UsbDevice device = devices.values().iterator().next();
+        // Tries to get ownership of the device in case that another application use it.
+        if (usbManager.hasPermission(device)) {
             final UsbDeviceConnection connection = usbManager.openDevice(device);
-            if (connection == null) {
-                Assert.fail("Cannot open USB connection.");
-                return null;
-            }
             for (int i = 0; i < device.getInterfaceCount(); i++) {
                 // Since the test runs real environment, we need to call claim interface with
                 // force = true to rob interfaces from other applications.
@@ -87,40 +75,15 @@
                 connection.releaseInterface(device.getInterface(i));
             }
             connection.close();
-            return device;
         }
-    }
-
-    private static void requestPermission(
-            final TestResultInstrumentation instrumentation,
-            UsbManager usbManager,
-            UsbDevice device) throws InterruptedException {
-        if (usbManager.hasPermission(device)) {
-            return;
-        }
-        final CountDownLatch latch = new CountDownLatch(1);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-                instrumentation.getTargetContext().unregisterReceiver(this);
-            }
-        };
-        instrumentation.getTargetContext().registerReceiver(
-                receiver, new IntentFilter(ACTION_USB_PERMISSION));
-        usbManager.requestPermission(device, PendingIntent.getBroadcast(
-                instrumentation.getTargetContext(),
-                0 /* requstCode */,
-                new Intent(ACTION_USB_PERMISSION),
-                0 /* flags */));
-        latch.await();
-        Assert.assertTrue(usbManager.hasPermission(device));
+        manager.openDevice(device.getDeviceId());
+        return device;
     }
 
     private static void waitForStorages(
             TestResultInstrumentation instrumentation,
             MtpManager manager,
-            int deviceId) throws InterruptedException, IOException {
+            int deviceId) throws IOException {
         while (true) {
             MtpDeviceRecord device = null;
             for (final MtpDeviceRecord deviceCandidate : manager.getDevices()) {
@@ -134,7 +97,7 @@
             }
             if (device.roots.length == 0) {
                 instrumentation.show("Wait for storages.");
-                Thread.sleep(1000);
+                SystemClock.sleep(1000);
                 continue;
             }
             return;
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 0f34e9e..57ba2b6 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Meer inligting oor hierdie drukker"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Sommige drukdienste is gedeaktiveer."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Kies drukdiens"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Soek tans vir drukkers"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Geen drukdienste is geaktiveer nie"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kanselleer tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Drukkerfout by <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drukker het <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> geblokkeer"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>-druktake</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>-druktaak</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Kanselleer"</string>
     <string name="restart" msgid="2472034227037808749">"Herbegin"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met drukker nie"</string>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index a6e1abf..a2182fb 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ተጨማሪ የዚህ አታሚ መረጃ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"አንዳንድ የህትመት አገልግሎቶች ተሰናክለዋል።"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"የህትመት አገልግሎት ይምረጡ"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"አታሚዎችን በመፈለግ ላይ"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ምንም የህትመት አገልግሎቶች አልነቁም"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በመተው ላይ"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"የአታሚ ስህተት <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"አታሚ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን አግዷል"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> የህትመት ስራ</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> የህትመት ስራ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ይቅር"</string>
     <string name="restart" msgid="2472034227037808749">"እንደገና ጀምር"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ከአታሚ ጋር ምንም ግንኙነት የለም"</string>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index 0291b7d..eab1339 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -65,6 +65,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"مزيد من المعلومات حول هذه الطابعة"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"بعض خدمات الطباعة معطَّلة."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"اختر خدمة طباعة"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"البحث عن طابعات"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"لم يتم تمكين أي خدمات طباعة"</string>
@@ -73,14 +74,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"جارٍ إلغاء <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"خطا في الطابعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"رفضت الطابعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="zero"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> مهمة طباعة</item>
-      <item quantity="two">مهمتا طباعة (<xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>)</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> مهام طباعة</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> مهمة طباعة</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> من مهام الطباعة</item>
-      <item quantity="one">مهمة طباعة واحدة (<xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>)</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"إلغاء"</string>
     <string name="restart" msgid="2472034227037808749">"إعادة تشغيل"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"لا يوجد اتصال بالطابعة"</string>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index e162793..bff477d 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Bu printer haqqında daha ətraflı məlumat"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Bəzi çap xidmətləri deaktiv edilib."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Çap xidmətini seçin"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Printer axtarılır"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Heç bir çap xidməti aktiv edilməyib"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ləğv edilir"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer xətası <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> işini blokladı"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> çap işi</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> çap işi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Ləğv et"</string>
     <string name="restart" msgid="2472034227037808749">"Yenidən başlat"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printerə heç bir bağlantı yoxdur"</string>
diff --git a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
index d6e439c..b28aa29 100644
--- a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
+++ b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
@@ -62,6 +62,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Još informacija o ovom štampaču"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Neke usluge štampanja su onemogućene."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Izaberite uslugu štampanja"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Pretraga štampača"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nijedna usluga štampanja nije omogućena"</string>
@@ -70,11 +71,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazuje se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Greška štampača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Štampač je blokirao <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="few">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Otkaži"</string>
     <string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze sa štampačem"</string>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index c2393543..e8de8ea 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Още информация за този принтер"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Някои услуги за отпечатване са деактивирани."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Избиране на услуга за отпечатване"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Търсене на принтери"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Няма активирани услуги за отпечатване"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се анулира"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка в принтера при „<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтерът блокира при „<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Задания за отпечатване: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Задание за отпечатване: <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Отказ"</string>
     <string name="restart" msgid="2472034227037808749">"Рестартиране"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Няма връзка с принтера"</string>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index 17a1a35..a1ca494 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"এই মুদ্রকটির বিষয়ে আরো তথ্য"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"কিছু মুদ্রণ পরিষেবা অক্ষম করা হয়েছে৷"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"মুদ্রণ পরিষেবা চয়ন করুন"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"মুদ্রকগুলি অনুসন্ধান করা হচ্ছে"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"মুদ্রণ পরিষেবা সক্ষম নেই"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> বাতিল করা হচ্ছে"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> মুদ্রক ত্রুটি"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"মুদ্রক <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> অবরুদ্ধ করেছে"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> মুদ্রণ কার্যগুলি</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> মুদ্রণ কার্যগুলি</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"বাতিল করুন"</string>
     <string name="restart" msgid="2472034227037808749">"পুনর্সূচনা"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"মুদ্রকে কোনো সংযোগ নেই"</string>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 2551206..aa6f992 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Més informació sobre aquesta impressora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Hi ha serveis d\'impressió que estan desactivats."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Selecció del servei d\'impressió"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Cerca d\'impressores"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"No hi ha cap servei d\'impressió activat"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"S\'està cancel·lant <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error d\'impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impressora bloquejada <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tasques d\'impressió</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tasca d\'impressió</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel·la"</string>
     <string name="restart" msgid="2472034227037808749">"Reinicia"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 0bb48f8..4bc22d4 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Další informace o této tiskárně"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Některé tiskové služby nejsou aktivovány."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Zvolte službu tisku"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Vyhledávání tiskáren"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nejsou aktivovány žádné tiskové služby"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Rušení úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tiskárny u úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tiskárna blokuje úlohu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="many">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Tiskové úlohy: <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Zrušit"</string>
     <string name="restart" msgid="2472034227037808749">"Restartovat"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nelze se připojit k tiskárně"</string>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index a9d042b..b8be624 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Flere oplysninger om denne printer"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Nogle udskrivningstjenester er deaktiveret."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Vælg udskriftstjeneste"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Søger efter printere"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ingen udskrivningstjenester er aktiveret"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annulleres"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Udskriften <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> mislykkedes"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeren har blokeret <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>-udskriftsjob</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>-udskriftsjob</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuller"</string>
     <string name="restart" msgid="2472034227037808749">"Genstart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse til printer"</string>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index 4eb5d6a9..0b77186 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Weitere Informationen über diesen Drucker"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Einige Druckdienste sind deaktiviert."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Druckdienst auswählen"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Suche nach Druckern"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Keine Druckdienste aktiviert"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird abgebrochen..."</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Druckerfehler <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drucker hat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> blockiert."</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Druckaufträge \"<xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>\"</item>
-      <item quantity="one">Druckauftrag \"<xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>\"</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Abbrechen"</string>
     <string name="restart" msgid="2472034227037808749">"Neu starten"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Keine Verbindung zum Drucker"</string>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index cd35785..d9a4aeb 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Περισσότερες πληροφορίες σχετικά με αυτόν τον εκτυπωτή"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Κάποιες υπηρ. εκτύπωσης είναι απενεργοποιημένες."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Επιλέξτε υπηρεσία εκτύπωσης"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Αναζήτηση για εκτυπωτές"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Δεν έχουν ενεργοποιηθεί υπηρεσίες εκτύπωσης"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ακύρωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Σφάλμα εκτυπωτή <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Ο εκτυπωτής απέκλεισε <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> εργασίες εκτύπωσης</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> εργασία εκτύπωσης</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Ακύρωση"</string>
     <string name="restart" msgid="2472034227037808749">"Επανεκκίνηση"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Δεν υπάρχει σύνδεση με εκτυπωτή"</string>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index 753d9df..d8a9437 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"More information about this printer"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Some print services are disabled."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blocked <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> print jobs</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> print job</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel"</string>
     <string name="restart" msgid="2472034227037808749">"Restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 753d9df..d8a9437 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"More information about this printer"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Some print services are disabled."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blocked <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> print jobs</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> print job</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel"</string>
     <string name="restart" msgid="2472034227037808749">"Restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 753d9df..d8a9437 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"More information about this printer"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Some print services are disabled."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blocked <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> print jobs</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> print job</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancel"</string>
     <string name="restart" msgid="2472034227037808749">"Restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 1a0d5d8..19c695b 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Más información sobre esta impresora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Hay servicios de impresión inhabilitados."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Elegir servicio de impresión"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"La impresora bloqueó <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>."</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Trabajos de impresión: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Trabajo de impresión: <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora."</string>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index eac568d..d13ccda 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Más información sobre esta impresora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Algunos servicios de impresión están inhabilitados."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Seleccionar servicio de impresión"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"La impresora ha bloqueado <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Trabajos de impresión <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Trabajo de impresión <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Volver a empezar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora"</string>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 2cde258..f03eb37 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Lisateave selle printeri kohta"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Mõned printimisteenused on keelatud."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Prinditeenuse valimine"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Printerite otsimine"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ühtegi printimisteenust pole lubatud"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> tühistamine"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri viga: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer blokeeris töö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> prinditööd</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> prinditöö</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Tühista"</string>
     <string name="restart" msgid="2472034227037808749">"Taaskäivita"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printeriühendus puudub"</string>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 96a3273..d4255e2 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Informazio gehiago inprimagailuari buruz"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Desgaituta daude inprimatzeko zerbitzu batzuk."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Aukeratu inprimatze-zerbitzua"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Inprimagailuak bilatzen"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ez dago gaituta inprimatzeko zerbitzurik"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bertan behera uzten"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Errorea <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzean"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Inprimag. <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> blokeatu du"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> inprimatze-lanak</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> inprimatze-lana</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Utzi"</string>
     <string name="restart" msgid="2472034227037808749">"Berrabiarazi"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Inprimagailua ez dago konektatuta"</string>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index fdc3989..907123c 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"اطلاعات بیشتر درباره چاپگر"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"بعضی از خدمات چاپ غیرفعال هستند."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"انتخاب سرویس چاپ"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"درحال جستجوی چاپگرها"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"هیچ خدمات چاپی فعال نیست"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"در حال لغو <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"خطای چاپگر <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"چاپگر، کار <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> را مسدود کرد"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">کار چاپ <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">کار چاپ <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"لغو"</string>
     <string name="restart" msgid="2472034227037808749">"راه‌اندازی مجدد"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"اتصال با چاپگر برقرار نیست"</string>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index 9267393..f57b884 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Lisätietoja tästä tulostimesta"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Osa tulostuspalveluista on poistettu käytöstä."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Valitse tulostuspalvelu"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Etsitään tulostimia"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ei käytössä olevia tulostuspalveluita"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Peruutetaan työ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Tulostinvirhe työlle <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tulostin esti työn <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tulostustyötä</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tulostustyö</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Peruuta"</string>
     <string name="restart" msgid="2472034227037808749">"Käynnistä uudelleen"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index bfb4862..949ba55 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Plus d\'information sur cette imprimante"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Certains services d\'impression sont désactivés."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Sélectionner le service d\'impression"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Recherche d\'imprimantes en cours..."</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Aucun service d\'impression activé"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression : « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impression de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> » bloquée"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâche d\'impression</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâches d\'impression</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuler"</string>
     <string name="restart" msgid="2472034227037808749">"Recommencer"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante"</string>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index de55e29..1fcc040 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Plus d\'informations sur cette imprimante"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Certains services d\'impression sont désactivés."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Sélectionner le service d\'impression"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Recherche d\'imprimantes en cours"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Aucun service d\'impression activé"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression pour \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" bloquée"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâche d\'impression</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tâches d\'impression</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuler"</string>
     <string name="restart" msgid="2472034227037808749">"Redémarrer"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante."</string>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index dc66084..2e60960 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Máis información sobre esta impresora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Algúns servizos de impresión están desactivados."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Escoller servizo de impresión"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Busca de impresoras"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Non hai servizos de impresión activados"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impresora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> traballos de impresión</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> traballo de impresión</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Non hai conexión coa impresora"</string>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index d05a392..4ba969c 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"આ પ્રિન્ટર વિશે વધુ માહિતી"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"કેટલીક છાપ સેવાઓ અક્ષમ કરેલ છે."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"પ્રિન્ટ સેવા પસંદ કરો"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"પ્રિન્ટર્સ માટે શોધી રહ્યું છે"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"કોઈ છાપ સેવાઓ સક્ષમ કરેલ નથી"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ને રદ કરી રહ્યું છે"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"પ્રિન્ટર ભૂલ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"પ્રિન્ટરે <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> અવરોધિત કર્યું"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> છાપ જોબ</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> છાપ જોબ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"રદ કરો"</string>
     <string name="restart" msgid="2472034227037808749">"પુનઃપ્રારંભ કરો"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"પ્રિન્ટર માટે કોઈ કનેક્શન નથી"</string>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 8051900..1061346 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"इस प्रिंटर के बारे में अधिक जानकारी"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"कुछ प्रिंट सेवाएं अक्षम हैं."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"प्रिंट सेवा चुनें"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर खोज रहा है"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"कोई भी प्रिंट सेवा सक्षम नहीं है"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द हो रहा है"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर त्रुटि <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटर अवरोधित <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> प्रिंट कार्य</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> प्रिंट कार्य</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"अभी नहीं"</string>
     <string name="restart" msgid="2472034227037808749">"पुन: आरंभ करें"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 4dab4cc..4a7d29f 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -62,6 +62,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Više informacija o ovom pisaču"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Neke su usluge ispisa onemogućene."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Odaberite uslugu ispisa"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Traženje pisača"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nije omogućena nijedna usluga ispisa"</string>
@@ -70,11 +71,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje zadatka <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Pogreška pisača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Pisač je blokirao <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadatak ispisa</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadatka ispisa</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadataka ispisa</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Odustani"</string>
     <string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze s pisačem"</string>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 1a56ee7..5aae2e4 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"További információ erről a nyomtatóról"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Egyes nyomtatási szolgáltatások le vannak tiltva."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Nyomtatási szolgáltatás kiválasztása"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Nyomtatók keresése"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nincs engedélyezett nyomtatási szolgáltatás"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> törlése"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Nyomtatási hiba: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> letiltva."</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> nyomtatási feladat</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> nyomtatási feladat</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Mégse"</string>
     <string name="restart" msgid="2472034227037808749">"Újraindítás"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nincs kapcsolat a nyomtatóval"</string>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 7b99dcf..179c384 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Հավելյալ տեղեկություններ այս տպիչի մասին"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Տպելու որոշ ծառայությունները կասեցված են:"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Ընտրեք տպելու ծառայությունը"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Տպիչների որոնում"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ակտիվացված տպման ծառայություններ չկան"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ը չեղարկվում է"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Տպիչի սխալ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Տպիչն արգելափակել է <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ը"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> տպման աշխատանք</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> տպման աշխատանք</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Չեղարկել"</string>
     <string name="restart" msgid="2472034227037808749">"Վերագործարկել"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Տպիչի հետ կապ չկա"</string>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index a991272..7286f7a 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Informasi selengkapnya tentang printer ini"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Beberapa layanan cetak dinonaktifkan."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Pilih layanan cetak"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Mencari printer"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Tidak ada layanan cetak yang aktif"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ada kesalahan printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer memblokir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Tugas cetak <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Tugas cetak <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Batal"</string>
     <string name="restart" msgid="2472034227037808749">"Mulai Ulang"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index e93f702..9ea49a9 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Frekari upplýsingar um þennan prentara"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Hluti prentþjónustunnar er óvirkur."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Veldu prentþjónustu"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Leitar að prentara"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Engin prentþjónusta er virk"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hættir við <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Prentaravilla <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Prentari útilokaði <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> prentverk</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> prentverk</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Hætta við"</string>
     <string name="restart" msgid="2472034227037808749">"Endurræsa"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Engin tenging við prentara"</string>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index ffba353..c19d012 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Ulteriori informazioni su questa stampante"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alcuni servizi di stampa sono disattivati."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Scegli servizio di stampa"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Ricerca di stampanti"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Non è stato attivato alcun servizio di stampa"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Errore della stampante: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"La stampante ha bloccato <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> processi di stampa</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> processo di stampa</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annulla"</string>
     <string name="restart" msgid="2472034227037808749">"Riavvia"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nessun collegamento alla stampante"</string>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index 2ac1093..00bf27c 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"מידע נוסף על מדפסת זו"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"שירותי הדפסה מסוימים מושבתים."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"בחר שירות הדפסה"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"מחפש מדפסות"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"לא הופעלו שירותי הדפסה"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"מבטל את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"שגיאת מדפסת ב-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"המדפסת חסמה את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="two"> עבודות הדפסה <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="many"> עבודות הדפסה <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other"> עבודות הדפסה <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one"> עבודת הדפסה <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"בטל"</string>
     <string name="restart" msgid="2472034227037808749">"הפעל מחדש"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index 2c3c24d..e0fc79a 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"このプリンタの詳細"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"一部の印刷サービスは無効になっています。"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"印刷サービスの選択"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"プリンタの検索中"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"使用できる印刷サービスがありません"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>をキャンセルしています"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"プリンタエラー: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>をブロックしました"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>の印刷ジョブ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>の印刷ジョブ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"キャンセル"</string>
     <string name="restart" msgid="2472034227037808749">"再試行"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"プリンタに接続されていません"</string>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index 2b0285d..ad1468a 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> — <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"დამატებითი ინფორმაცია ამ პრინტერის შესახებ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"ბეჭდვის ზოგიერთი სერვისი გათიშულია."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"აირჩიეთ ბეჭდვის სერვისი"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"მიმდინარეობს პრინტერების ძიება"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ბეჭდვის სერვისები გააქტიურებული არ არის"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"მიმდინარეობს <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ის გაუქმება"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ბეჭდვის შეცდომა <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"პრინტერმა დაბლოკა <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ბეჭდვის დავალება</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ბეჭდვის დავალება</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"გაუქმება"</string>
     <string name="restart" msgid="2472034227037808749">"გადატვირთვა"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"პრინტერთან კავშირი არ არის"</string>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index fc099c9..d0337a6 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Осы принтер туралы қосымша ақпарат"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Кейбір басып шығару қызметтері өшірілген."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Принтер қызметін таңдау"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Принтерлерді іздеу"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Басып шығару қызметтері қосылмаған"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> жұмысын тоқтатуда"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> принтер қателігі"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтер <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> жұмысын бөгеді"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> баспа тапсырмасы</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> баспа тапсырмасы</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Тоқтату"</string>
     <string name="restart" msgid="2472034227037808749">"Қайта бастау"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Принтермен байланыс жоқ"</string>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index b51091e..c9431e9 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ព័ត៌មានបន្ថែមអំពីម៉ាស៊ីបោះពុម្ពនេះ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"សេវាកម្មបោះពុម្ពមួយចំនួនត្រូវបានបិទដំណើរការ"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"ជ្រើស​សេវា​បោះពុម្ព"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"ស្វែងរក​ម៉ាស៊ីន​បោះពុម្ព"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"គ្មានការបើកដំណើរការសេវាបោះពុម្ពទេ"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"ការ​បោះបង់ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"កំហុស​ម៉ាស៊ីន​បោះពុម្ព <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ម៉ាស៊ីន​បោះពុម្ព​បាន​ទប់ស្កាត់ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">ការងារបោះពុម្ព <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">ការងារបោះពុម្ព <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index 5d5dee8..fc5149a 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ಈ ಪ್ರಿಂಟರ್ ಬಗ್ಗೆ ಇನ್ನಷ್ಟು ಮಾಹಿತಿ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"ಕೆಲವು ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"ಮುದ್ರಣ ಸೇವೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"ಪ್ರಿಂಟರ್‌‌ಗಳಿಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ಯಾವುದೇ ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿಲ್ಲ"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ರದ್ದು ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ಮುದ್ರಕ ದೋಷ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ಮುದ್ರಕವು <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ನಿರ್ಬಂಧಿಸಿದೆ"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ಮುದ್ರಣ ಕಾರ್ಯಗಳು</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ಮುದ್ರಣ ಕಾರ್ಯಗಳು</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ರದ್ದುಮಾಡು"</string>
     <string name="restart" msgid="2472034227037808749">"ಮರುಪ್ರಾರಂಭಿಸು"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ಮುದ್ರಕಕ್ಕೆ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 98617e7..2faff1f 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"이 프린터에 대한 정보 더보기"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"프린트 서비스 일부가 사용 중지되었습니다."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"인쇄 서비스 선택"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"프린터 검색 중"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"사용 가능한 프린트 서비스 없음"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 취소 중"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"프린터 오류: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"차단된 프린터: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> 인쇄 작업</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> 인쇄 작업</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"취소"</string>
     <string name="restart" msgid="2472034227037808749">"다시 시작"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"프린터와 연결되지 않음"</string>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 2a11ff8..a01e4a8 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Бул принтер жөнүндө көбүрөөк маалымат"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Басып чыгаруу кызматтарынын айрымы өчүрүлгөн."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Принтер кызматын тандоо"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Принтерлер изделүүдө"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Принтер-кызматтары иштетилген эмес"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> токтотулууда"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Принтерде ката кетти: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтер бөгөттөдү: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> басуу тапшырмасы</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> басуу тапшырмасы</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Айнуу"</string>
     <string name="restart" msgid="2472034227037808749">"Кайра баштоо"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер менен байланыш жок"</string>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 788e5aa..b5d13b5 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ຂໍ້ມູນເພີ່ມເຕີມກ່ຽວກັບເຄື່ອງພິມນີ້"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"ບາງການບໍລິການພິມຖືກປິດນຳໃຊ້."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"ເລືອກບໍລິການການພິມ"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"ກຳລັງຊອກຫາເຄື່ອງພິມ"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ບໍ່​ມີ​ການ​ບໍ​ລິ​ການ​ພິມ​ເປີດ​ໃຊ້​ງານ​ໄວ້"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"ກຳລັງຍົກເລີກ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ເຄື່ອງພິມເກີດຂໍ້ຜິດພາດ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ເຄື່ອງພິມຖືກບລອກ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ງານ​ພິມ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ງານ​ພິມ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ຍົກເລີກ"</string>
     <string name="restart" msgid="2472034227037808749">"ປິດເປີດໃໝ່"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ບໍ່ມີການເຊື່ອມຕໍ່ຫາເຄື່ອງພິມ"</string>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 1826e8e..3b8f143 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"„<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g>“ – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Daugiau informacijos apie šį spausdintuvą"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Kai kurios spausdinimo paslaugos išjungtos."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Pasirinkite spausdinimo paslaugą"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Ieškoma spausdintuvų"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Neįgalinta jokių spausdinimo paslaugų"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Atšaukiama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Spausdintuvo klaida: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Spausdintuvas užblokavo: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduotis</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduotys</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduoties</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> spausdinimo užduočių</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Atšaukti"</string>
     <string name="restart" msgid="2472034227037808749">"Paleisti iš naujo"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nėra ryšio su spausdintuvu"</string>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 5c17efe..762d0bd 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -62,6 +62,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> — <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Plašāka informācija par šo printeri"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Daži drukas pakalpojumi ir atspējoti."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Izvēlieties drukāšanas pakalpojumu"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Printeru meklēšana"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nav iespējots neviens drukas pakalpojums"</string>
@@ -70,11 +71,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Pārtrauc drukas darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printera kļūda ar darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeris bloķēja darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="zero"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> drukas darbi</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> drukas darbs</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> drukas darbi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Atcelt"</string>
     <string name="restart" msgid="2472034227037808749">"Restartēt"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nav savienojuma ar printeri"</string>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index ebc1181..de6d3e9 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Повеќе информации за овој печатач"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Некои услуги за печатење се оневозможени."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Избери услуга печатење"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Пребарување печатачи"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Нема овозможени услуги за печатење"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се откажува"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка при печатење <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Печатачот го блокираше <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> работа за печатење</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> работи за печатење</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Откажи"</string>
     <string name="restart" msgid="2472034227037808749">"Рестартирај"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нема поврзување со печатач"</string>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index c08a3d4..7a33e14 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ഈ പ്രിന്ററിനെ കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"ചില പ്രിന്റ് സേവനങ്ങൾ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"പ്രിന്റ് സേവനം തിരഞ്ഞെടുക്കുക"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"പ്രിന്ററുകൾക്കായി തിരയുന്നു"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"പ്രിന്റ് സേവനങ്ങളൊന്നും പ്രവർത്തനക്ഷമാക്കിയിട്ടില്ല"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> റദ്ദാക്കുന്നു"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"പ്രിന്റർ പിശക് <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"പ്രിന്റർ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> തടഞ്ഞു"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> പ്രിന്റ് ജോലികൾ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> പ്രിന്റ് ജോലി</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"റദ്ദാക്കുക"</string>
     <string name="restart" msgid="2472034227037808749">"പുനരാരംഭിക്കുക"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"പ്രിന്ററിൽ കണക്ഷനൊന്നുമില്ല"</string>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index dcef28f..c94e56d 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Энэ хэвлэгчийн талаарх дэлгэрэнгүй мэдээлэл"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Зарим хэвлэх үйлчилгээг идэвхгүй болгосон байна."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Хэвлэх үйлчилгээг сонгох"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Принтер хайж байна"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Хэвлэх үйлчилгээг идэвхжүүлээгүй"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Цуцлаж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Принтерийн алдаа <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Принтер хориглогдсон <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ажлыг хэвлэх</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ажлыг хэвлэх</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Цуцлах"</string>
     <string name="restart" msgid="2472034227037808749">"Дахин эхлүүлэх"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер холбогдоогүй байна"</string>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index 384f0de..ab25010 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"या प्रिंटर विषयी अधिक माहिती"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"काही मुद्रण सेवा अक्षम केल्या आहेत."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"मुद्रण सेवा निवडा"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही मुद्रण सेवा सक्षम केलेल्या नाहीत"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द करीत आहे"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर त्रुटी <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिंटरने <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> अवरोधित केले"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> मुद्रण कार्य</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> मुद्रण कार्ये</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"रद्द करा"</string>
     <string name="restart" msgid="2472034227037808749">"रीस्टार्ट करा"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्‍शन नाही"</string>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 19a6e76..917ae8a 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Maklumat lanjut tentang pencetak ini"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Sesetengah perkhidmatan cetak dilumpuhkan."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Pilih perkhidmatan cetak"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Mencari pencetak"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Perkhidmatan cetak tidak didayakan"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ralat pencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Pencetak disekat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Kerja cetakan <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Kerja cetakan <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Batal"</string>
     <string name="restart" msgid="2472034227037808749">"Mulakan semula"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index d3c0672..4d4c95b 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ဤပရင်တာ အကြောင်း ပိုမိုလေ့လာပါ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"ပရင့်ထုတ်ရေး အချို့ဝန်ဆောင်မှုများကို ပိတ်ထားပါသည်။"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"စာထုတ်ရန် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"စာထုတ်စက်များကို ရှာနေပါသည်"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ပုံနှိပ်ထုတ်ယူရေး ဝန်ဆောင်မှုများ ဖွင့်မထားပါ"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို ပယ်ဖျက်နေပါသည်"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"စာထုတ်စက်မှ အမှား <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ကိုစာထုတ်စက်ကငြင်းလိုက်သည်"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> စာထုတ်စရာများ</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>စာထုတ်စရာ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ပယ်ဖျက်"</string>
     <string name="restart" msgid="2472034227037808749">"အစက ပြန်စရန်"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index c34e7bc..9efa5d1 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Mer informasjon om denne printeren"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Noen utskriftstjenester er slått av."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Velg utskriftstjeneste"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Søker etter skrivere"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ingen utskriftstjenester er slått på"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Skriverfeil <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Skriveren blokkerte <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> utskriftsjobber</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> utskriftsjobb</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Avbryt"</string>
     <string name="restart" msgid="2472034227037808749">"Start på nytt"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse med skriveren"</string>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index d1959d9..281a65d 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"यस प्रिन्टरको बारेमा थप जानकारी"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"केही मुद्रण सेवाहरू असक्षम छन्।"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"प्रिन्ट सेवा छनौट गर्नुहोस्"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिन्टरहरू खोज्दै"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"कुनै पनि मुद्रण सेवाहरू सक्रिय छैनन्"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"रद्द गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिन्टर त्रुटि <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"प्रिन्टर ब्लक गरियो <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> कार्यहरू प्रिन्ट गर्नुहोस्</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> कार्य प्रिन्ट गर्नुहोस्</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"रद्द गर्नुहोस्"</string>
     <string name="restart" msgid="2472034227037808749">"पुनःस्टार्ट गर्नुहोस्"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिन्टरमा कुनै जडान छैन"</string>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 5df3298..eef9880 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Meer informatie over deze printer"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Sommige afdrukservices zijn uitgeschakeld."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Afdrukservice kiezen"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Printers zoeken"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Geen afdrukservices ingeschakeld"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annuleren"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printerfout <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> geblokkeerd door printer"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> afdruktaken</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> afdruktaak</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Annuleren"</string>
     <string name="restart" msgid="2472034227037808749">"Opnieuw starten"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met printer"</string>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index 57e9969..7d7860c 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ਇਸ ਪ੍ਰਿੰਟਰ ਬਾਰੇ ਹੋਰ ਜਾਣਕਾਰੀ"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"ਕੁਝ ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਅਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ।"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"ਪ੍ਰਿੰਟ ਸੇਵਾ ਚੁਣੋ"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"ਪ੍ਰਿੰਟਰ ਖੋਜ ਰਿਹਾ ਹੈ"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਯੋਗ ਨਹੀਂ ਬਣਾਈਆਂ ਗਈਆਂ"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਰੱਦ ਕਰ ਰਿਹਾ ਹੈ"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ਪ੍ਰਿੰਟਰ ਅਸ਼ੁੱਧੀ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ਪ੍ਰਿੰਟਰ ਬਲੌਕ ਕੀਤਾ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ਪ੍ਰਿੰਟ ਜੌਬਸ</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ਪ੍ਰਿੰਟ ਜੌਬਸ</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ਰੱਦ ਕਰੋ"</string>
     <string name="restart" msgid="2472034227037808749">"ਰੀਸਟਾਰਟ ਕਰੋ"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ਪ੍ਰਿੰਟਰ ਲਈ ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 4439acb..6837edf 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Więcej informacji o tej drukarce"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Niektóre usługi drukowania są wyłączone."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Wybierz usługę drukowania"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Szukanie drukarek"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Brak włączonych usług drukowania"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Anulowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Błąd drukarki: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Drukarka zablokowała <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadania drukowania</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadań drukowania</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> zadania drukowania</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> zadanie drukowania</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Anuluj"</string>
     <string name="restart" msgid="2472034227037808749">"Od nowa"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Brak połączenia z drukarką"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 63bb868..c9713c9 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Mais informações sobre essa impressora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alguns serviços de impressão estão desativados."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Selecione o serviço de impressão"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index d364ef4..9fabc0f 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Mais informações acerca desta impressora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alguns serviços de impressão estão desativados."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Escolher o serviço de impressão"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"A procurar impressoras"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tarefas de impressão</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tarefa de impressão</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Sem ligação à impressora"</string>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 63bb868..c9713c9 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Mais informações sobre essa impressora"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alguns serviços de impressão estão desativados."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Selecione o serviço de impressão"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"A impressora bloqueou <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Tarefas de impressão <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Cancelar"</string>
     <string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 51dfe7a..7364eb0 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -62,6 +62,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Mai multe informații despre această imprimantă"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Unele servicii de printare sunt dezactivate."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Alegeți serviciul de printare"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Se caută imprimante"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Niciun serviciu de printare activat"</string>
@@ -70,11 +71,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Se anulează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Eroare de printare: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printare blocată: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> activități de printare</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> de activități de printare</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> activitate de printare</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Anulați"</string>
     <string name="restart" msgid="2472034227037808749">"Reporniți"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 6ba1046..bb03860 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Подробные сведения о принтере"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Некоторые сервисы печати отключены."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Выберите службу печати"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Поиск принтеров…"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Службы печати недоступны"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отмена задания <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Ошибка задания \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Задание \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" заблокировано"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="few">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="many">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Задания печати: <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Отмена"</string>
     <string name="restart" msgid="2472034227037808749">"Повторить"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 4908ea5..610442d 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"මෙම මුද්‍රණ යන්ත්‍රය ගැන තවත් තොරතුරු"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"සමහර මුද්‍රණ සේවා අබලයි."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"මුද්‍රණ සේවාව තෝරන්න"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"මුද්‍රණ යන්ත්‍ර සොයමින්"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"මුද්‍රණ සේවා සබල නැත"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"අවලංගු කෙරේ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"මුද්‍රණ දෝෂය <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"මුද්‍රණ යන්ත්‍රය <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> අවුරා ඇති"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">මුද්‍රණ කාර්ය <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">මුද්‍රණ කාර්ය <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"අවලංගු කරන්න"</string>
     <string name="restart" msgid="2472034227037808749">"යළි අරඹන්න"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"මුද්‍රණ යන්ත්‍රය වෙත සම්බන්ධය නැත"</string>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 418363d..603d1d2 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Ďalšie informácie o tejto tlačiarni"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Niektoré tlačové služby sú vypnuté."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Výber tlačovej služby"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Vyhľadávanie tlačiarní"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Žiadne tlačové služby nie sú aktivované"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prebieha zrušenie úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tlačiarne – úloha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tlačiareň zablok. úlohu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tlačové úlohy</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tlačovej úlohy</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tlačových úloh</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> tlačová úloha</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Zrušiť"</string>
     <string name="restart" msgid="2472034227037808749">"Spustiť znova"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index e2be161..4a08269 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Več informacij o tem tiskalniku"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Nekatere tiskalne storitve so onemogočene."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Izberite tiskalno storitev"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Iskanje tiskalnikov"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ni omogočenih tiskalnih storitev"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Preklic: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Napaka tiskalnika: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Tiskalnik je blokiral <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalno opravilo</item>
-      <item quantity="two"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalni opravili</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalna opravila</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> tiskalnih opravil</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Prekliči"</string>
     <string name="restart" msgid="2472034227037808749">"Začni znova"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ni povezave s tiskalnikom"</string>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index d5ebf32..b0902ef 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Më shumë informacione mbi këtë printer"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Disa shërbime printimi janë çaktivizuar."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Zgjidh shërbimin e printimit"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Po kërkon për printerë"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Nuk ka shërbime printimi të aktivizuara"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Po anulon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri ndeshi në gabim gjatë punës: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printeri bllokoi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> punë për printim</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> punë për printim</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Anulo"</string>
     <string name="restart" msgid="2472034227037808749">"Rifillo"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printeri nuk është i lidhur"</string>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 166e5dd..feb2940 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -62,6 +62,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Још информација о овом штампачу"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Неке услуге штампања су онемогућене."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Изаберите услугу штампања"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Претрага штампача"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Ниједна услуга штампања није омогућена"</string>
@@ -70,11 +71,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отказује се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка штампача <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Штампач је блокирао <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one">Задаци штампања <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="few">Задаци штампања <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="other">Задаци штампања <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Откажи"</string>
     <string name="restart" msgid="2472034227037808749">"Поново покрени"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Нема везе са штампачем"</string>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 033d583..cf398c7 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Mer information om den här skrivaren"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Några utskriftstjänster har inaktiverats."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Välj utskriftstjänst"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Söker efter skrivare"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Inga utskriftstjänster har aktiverats"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Skrivarfel för <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Skrivaren har blockerat <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> utskriftsjobb</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> utskriftsjobb</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Avbryt"</string>
     <string name="restart" msgid="2472034227037808749">"Starta om"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen anslutning till skrivaren"</string>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 0e2dcdd..7e00b70 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Maelezo zaidi kuhusu printa hii"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Baadhi ya huduma za uchapishaji zimezimwa."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Chagua huduma ya printa"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Inatafuta printa"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Huduma za kuchapisha hazijawashwa"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Inaghairi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Hitilafu ya kuchapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printa imefungwa <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Kazi ya kuchapisha ya <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Kazi ya kuchapisha ya <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> </item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Ghairi"</string>
     <string name="restart" msgid="2472034227037808749">"Anzisha upya"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index 2e90d38..ae0b774 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"இந்தப் பிரிண்டர் பற்றிய கூடுதல் தகவல்"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"சில அச்சுப் பொறிகள் முடக்கப்பட்டன."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"அச்சுப் பொறியைத் தேர்வுசெய்யவும்"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"அச்சுப்பொறிகளைத் தேடுகிறது"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"அச்சுப் பொறிகள் இல்லை"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ ரத்துசெய்கிறது"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"பிரிண்டர் பிழை <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"பிரிண்டர் <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐத் தடுத்தது"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> அச்சுப் பணிகள்</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> அச்சுப் பணி</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ரத்துசெய்"</string>
     <string name="restart" msgid="2472034227037808749">"மீண்டும் தொடங்கு"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"அச்சுப்பொறியுடன் இணைக்கப்படவில்லை"</string>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index 6bdbd5c..5fd8d60 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ఈ ప్రింటర్ గురించి మరింత సమాచారం"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"కొన్ని ముద్రణ సేవలు నిలిపివేయబడ్డాయి."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"ముద్రణ సేవను ఎంచుకోండి"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"ప్రింటర్‌ల కోసం శోధిస్తోంది"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ముద్రణ సేవలు ఏవీ ప్రారంభించలేదు"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను రద్దు చేస్తోంది"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ప్రింటర్ లోపం <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"ప్రింటర్ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను బ్లాక్ చేసింది"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ముద్రణ జాబ్‌లు</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> ముద్రణ జాబ్</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"రద్దు చేయి"</string>
     <string name="restart" msgid="2472034227037808749">"పునఃప్రారంభించు"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ప్రింటర్‌కు కనెక్షన్ లేదు"</string>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index a581357..ebd5e2a 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"ข้อมูลเพิ่มเติมเกี่ยวกับเครื่องพิมพ์นี้"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"บริการพิมพ์บางอย่างถูกปิดใช้"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"เลือกบริการพิมพ์"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"กำลังค้นหาเครื่องพิมพ์"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"ไม่ได้เปิดใช้บริการพิมพ์"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"กำลังยกเลิก <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"ข้อผิดพลาดเครื่องพิมพ์ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"เครื่องพิมพ์ได้บล็อก <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> งานพิมพ์</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> งานพิมพ์</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"ยกเลิก"</string>
     <string name="restart" msgid="2472034227037808749">"เริ่มต้นใหม่"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"ไม่มีการเชื่อมต่อไปยังเครื่องพิมพ์"</string>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 325ce8c..ebe869b 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Higit pang impormasyon tungkol sa printer na ito"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Naka-disable ang ilang serbisyo sa pag-print."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Pumili ng serbisyo ng pag-print"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Naghahanap ng mga printer"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Walang mga naka-enable na serbisyo sa pag-print"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kinakansela ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Error sa printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Naka-block ang Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> ipi-print</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> na ipi-print</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Kanselahin"</string>
     <string name="restart" msgid="2472034227037808749">"I-restart"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Hindi nakakonekta sa printer"</string>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index d945979..9cd42ab 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Bu yazıcıyla ilgili daha fazla bilgi"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Bazı yazdırma hizmetleri devre dışı."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Yazdırma hizmetini seçin"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Yazıcılar aranıyor"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Etkin yazıcı hizmeti yok"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> iptal ediliyor"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Yazıcı hatası: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Yazıcı <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> işini engelledi"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> yazdırma işi</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> yazdırma işi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"İptal"</string>
     <string name="restart" msgid="2472034227037808749">"Yeniden başlat"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index ffdfde0..1082147 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -63,6 +63,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Докладніше про цей принтер"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Деякі служби друку вимкнено."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Вибрати службу друку"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Пошук принтерів"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Немає служб друку"</string>
@@ -71,12 +72,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" скасовується"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Помилка завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" заблоковано"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдання друку</item>
-      <item quantity="few"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдання друку</item>
-      <item quantity="many"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдань друку</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> завдань друку</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Скасувати"</string>
     <string name="restart" msgid="2472034227037808749">"Перезапустити"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Немає з’єднання з принтером"</string>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index 72a6ab9f..56f1093 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"اس پرنٹر کے بارے میں مزید معلومات"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"پرنٹ کی کچھ سروسز غیر فعال ہیں۔"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"پرنٹ سروس منتخب کریں"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"پرنٹرز تلاش کر رہا ہے"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"کوئی پرنٹ سروس فعال نہیں"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> کو منسوخ کر رہا ہے"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"پرنٹر کی خرابی <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"پرنٹر نے <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> کو مسدود کر دیا"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> پرنٹ جابز</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> پرنٹ جاب</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"منسوخ کریں"</string>
     <string name="restart" msgid="2472034227037808749">"دوبارہ شروع کریں"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"پرنٹر کے ساتھ کوئی کنکشن نہیں ہے"</string>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index c7b4263..30b218e 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Printer haqida batafsil ma’lumot"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Bir qancha chop etish xizmatlari o‘chirilgan."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Chop etish xizmatini tanlang"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Printerlar qidirilmoqda"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Hech qaysi chop etish xizmati yoqilmagan"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bekor qilinmoqda"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Printerda xatolik: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ni taqiqladi"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> chop qilish vazifalari</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> chop qilish vazifasi</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Bekor qilish"</string>
     <string name="restart" msgid="2472034227037808749">"Qayta boshlash"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Printer ulanmagan"</string>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index 771d57c..32aaf63 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Thông tin khác về máy in này"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Một số dịch vụ in bị vô hiệu hóa."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Chọn dịch vụ in"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Đang tìm kiếm máy in"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Chưa kích hoạt dịch vụ in nào"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hủy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Lỗi máy in <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Máy in đã chặn <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">Lệnh in <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
-      <item quantity="one">Lệnh in <xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g></item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Hủy"</string>
     <string name="restart" msgid="2472034227037808749">"Bắt đầu lại"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Không có kết nối nào với máy in"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index bea91d7..42cf3b1 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"关于此打印机的更多信息"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"部分打印服务已停用。"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"选择打印服务"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜索打印机"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"未启用任何打印服务"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"打印机在打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”时出错"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"打印机拒绝打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other">“<xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g>”打印作业</item>
-      <item quantity="one">“<xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g>”打印作业</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"取消"</string>
     <string name="restart" msgid="2472034227037808749">"重新开始"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 4fbef0d..0a458ad 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"此打印機詳情"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"已停用部分列印服務。"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"選擇列印服務"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜尋打印機"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"沒有已啟用的列印服務"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"打印機錯誤:<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"打印機已封鎖 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> 項列印工作</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> 項列印工作</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"取消"</string>
     <string name="restart" msgid="2472034227037808749">"重新開始"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與打印機連線"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index 2fdcaac..7a30011 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"查看這台印表機的詳細資訊"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"部分列印服務已停用。"</string>
     <string name="choose_print_service" msgid="3740309762324459694">"選擇列印服務"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜尋印表機"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"未啟用任何列印服務"</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"印表機發生錯誤:<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"印表機封鎖了 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> 個列印工作</item>
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_0">%1$d</xliff:g> 個列印工作</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"取消"</string>
     <string name="restart" msgid="2472034227037808749">"重新開始"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與印表機建立連線"</string>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 92595aa..f57b58c 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -61,6 +61,7 @@
     </plurals>
     <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="printer_info_desc" msgid="7181988788991581654">"Olunye ulwazi mayelana nale phrinta"</string>
+    <string name="print_services_disabled_toast" msgid="1205302482388937547">"Amanye amasevisi wokuphrinta akhutshaziwe."</string>
     <string name="choose_print_service" msgid="3740309762324459694">"Khetha isevisi yephrinta"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"Isesha amaphrinta"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"Amasevisi ephrinta akavuliwe."</string>
@@ -69,10 +70,6 @@
     <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ikhansela i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="failed_notification_title_template" msgid="2256217208186530973">"Iphutha lephrinta ye-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
     <string name="blocked_notification_title_template" msgid="1175435827331588646">"Iphrinta engu-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ivinjelwe"</string>
-    <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
-      <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> imisebenzi yokuphrinta</item>
-      <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> imisebenzi yokuphrinta</item>
-    </plurals>
     <string name="cancel" msgid="4373674107267141885">"Khansela"</string>
     <string name="restart" msgid="2472034227037808749">"Qala kabusha"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"Akukho ukuxhumana kuphrinta"</string>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 97a7bff..76292a1 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -150,6 +150,9 @@
     <!-- Description of printer info icon. [CHAR LIMIT=50] -->
     <string name="printer_info_desc">More information about this printer</string>
 
+    <!-- Notification that print services as disabled. [CHAR LIMIT=50] -->
+    <string name="print_services_disabled_toast">Some print services are disabled.</string>
+
     <!-- Add printer dialog  -->
 
     <!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
@@ -178,12 +181,6 @@
     <!-- Template for the notification label for a blocked print job. [CHAR LIMIT=25] -->
     <string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
 
-    <!-- Template for the notification label for a composite (multiple items) print jobs notification. [CHAR LIMIT=25] -->
-    <plurals name="composite_notification_title_template">
-        <item quantity="one"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print job</item>
-        <item quantity="other"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print jobs</item>
-    </plurals>
-
     <!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
     <string name="cancel">Cancel</string>
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 82fd512..0210693 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -40,6 +40,7 @@
 import android.print.PrintJobInfo;
 import android.print.PrintManager;
 import android.provider.Settings;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.printspooler.R;
@@ -61,13 +62,22 @@
 
     private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
 
+    private static final String PRINT_JOB_NOTIFICATION_GROUP_KEY = "PRINT_JOB_NOTIFICATIONS";
+    private static final String PRINT_JOB_NOTIFICATION_SUMMARY = "PRINT_JOB_NOTIFICATIONS_SUMMARY";
+
     private final Context mContext;
     private final NotificationManager mNotificationManager;
 
+    /**
+     * Mapping from printJobIds to their notification Ids.
+     */
+    private final ArraySet<PrintJobId> mNotifications;
+
     public NotificationController(Context context) {
         mContext = context;
         mNotificationManager = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotifications = new ArraySet<>(0);
     }
 
     public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
@@ -81,16 +91,44 @@
             }
         }
 
-        updateNotification(notifyPrintJobs);
+        updateNotifications(notifyPrintJobs);
     }
 
-    private void updateNotification(List<PrintJobInfo> printJobs) {
-        if (printJobs.size() <= 0) {
-            removeNotification();
-        } else if (printJobs.size() == 1) {
-            createSimpleNotification(printJobs.get(0));
-        } else {
+    /**
+     * Update notifications for the given print jobs, remove all other notifications.
+     *
+     * @param printJobs The print job that we want to create notifications for.
+     */
+    private void updateNotifications(List<PrintJobInfo> printJobs) {
+        ArraySet<PrintJobId> removedPrintJobs = new ArraySet<>(mNotifications);
+
+        final int numPrintJobs = printJobs.size();
+
+        // Create summary notification
+        if (numPrintJobs > 1) {
             createStackedNotification(printJobs);
+        } else {
+            mNotificationManager.cancel(PRINT_JOB_NOTIFICATION_SUMMARY, 0);
+        }
+
+        // Create per print job notification
+        for (int i = 0; i < numPrintJobs; i++) {
+            PrintJobInfo printJob = printJobs.get(i);
+            PrintJobId printJobId = printJob.getId();
+
+            removedPrintJobs.remove(printJobId);
+            mNotifications.add(printJobId);
+
+            createSimpleNotification(printJob);
+        }
+
+        // Remove notifications for print jobs that do not exist anymore
+        final int numRemovedPrintJobs = removedPrintJobs.size();
+        for (int i = 0; i < numRemovedPrintJobs; i++) {
+            PrintJobId removedPrintJob = removedPrintJobs.valueAt(i);
+
+            mNotificationManager.cancel(removedPrintJob.flattenToString(), 0);
+            mNotifications.remove(removedPrintJob);
         }
     }
 
@@ -148,7 +186,8 @@
                 .setOngoing(true)
                 .setShowWhen(true)
                 .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY);
 
         if (firstAction != null) {
             builder.addAction(firstAction);
@@ -158,11 +197,14 @@
             builder.addAction(secondAction);
         }
 
-        if (printJob.getState() == PrintJobInfo.STATE_STARTED) {
+        if (printJob.getState() == PrintJobInfo.STATE_STARTED
+                || printJob.getState() == PrintJobInfo.STATE_QUEUED) {
             float progress = printJob.getProgress();
             if (progress >= 0) {
-                builder.setProgress(Integer.MAX_VALUE, (int)(Integer.MAX_VALUE * progress),
+                builder.setProgress(Integer.MAX_VALUE, (int) (Integer.MAX_VALUE * progress),
                         false);
+            } else {
+                builder.setProgress(Integer.MAX_VALUE, 0, true);
             }
         }
 
@@ -173,7 +215,7 @@
             builder.setContentText(printJob.getPrinterName());
         }
 
-        mNotificationManager.notify(0, builder.build());
+        mNotificationManager.notify(printJob.getId().flattenToString(), 0, builder.build());
     }
 
     private void createPrintingNotification(PrintJobInfo printJob) {
@@ -201,33 +243,36 @@
                 .setContentIntent(createContentIntent(null))
                 .setWhen(System.currentTimeMillis())
                 .setOngoing(true)
-                .setShowWhen(true);
+                .setShowWhen(true)
+                .setGroup(PRINT_JOB_NOTIFICATION_GROUP_KEY)
+                .setGroupSummary(true);
 
         final int printJobCount = printJobs.size();
 
         InboxStyle inboxStyle = new InboxStyle();
-        inboxStyle.setBigContentTitle(String.format(mContext.getResources().getQuantityText(
-                R.plurals.composite_notification_title_template,
-                printJobCount).toString(), printJobCount));
 
+        int icon = com.android.internal.R.drawable.ic_print;
         for (int i = printJobCount - 1; i>= 0; i--) {
             PrintJobInfo printJob = printJobs.get(i);
-            if (i == printJobCount - 1) {
-                builder.setLargeIcon(((BitmapDrawable) mContext.getResources().getDrawable(
-                        computeNotificationIcon(printJob), null)).getBitmap());
-                builder.setSmallIcon(computeNotificationIcon(printJob));
-                builder.setContentTitle(computeNotificationTitle(printJob));
-                builder.setContentText(printJob.getPrinterName());
-            }
+
             inboxStyle.addLine(computeNotificationTitle(printJob));
+
+            // if any print job is in an error state show an error icon for the summary
+            if (printJob.getState() == PrintJobInfo.STATE_FAILED
+                    || printJob.getState() == PrintJobInfo.STATE_BLOCKED) {
+                icon = com.android.internal.R.drawable.ic_print_error;
+            }
         }
 
+        builder.setSmallIcon(icon);
+        builder.setLargeIcon(
+                ((BitmapDrawable) mContext.getResources().getDrawable(icon, null)).getBitmap());
         builder.setNumber(printJobCount);
         builder.setStyle(inboxStyle);
         builder.setColor(mContext.getColor(
                 com.android.internal.R.color.system_notification_accent_color));
 
-        mNotificationManager.notify(0, builder.build());
+        mNotificationManager.notify(PRINT_JOB_NOTIFICATION_SUMMARY, 0, builder.build());
     }
 
     private String computeNotificationTitle(PrintJobInfo printJob) {
@@ -261,10 +306,6 @@
         }
     }
 
-    private void removeNotification() {
-        mNotificationManager.cancel(0);
-    }
-
     private PendingIntent createContentIntent(PrintJobId printJobId) {
         Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
         if (printJobId != null) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 496a0b0..18160ff 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -1416,9 +1416,9 @@
         }
 
         @Override
-        public void removeApprovedPrintService(ComponentName serviceToRemove) {
+        public void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
             (new ApprovedPrintServices(PrintSpoolerService.this))
-                    .removeApprovedService(serviceToRemove);
+                    .pruneApprovedServices(servicesToKeep);
         }
 
         @Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index ea11ae4..684a1de 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -173,15 +173,19 @@
         if (DEBUG) {
             Log.i(LOG_TAG, "[CALLED] start()");
         }
-        if (mState != STATE_INITIAL) {
-            throw new IllegalStateException("Cannot start in state:" + stateToString(mState));
-        }
-        try {
-            mPrintDocumentAdapter.start();
-            mState = STATE_STARTED;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error calling start()", re);
-            mState = STATE_FAILED;
+        if (mState == STATE_FAILED) {
+            Log.w(LOG_TAG, "Failed before start.");
+        } else {
+            if (mState != STATE_INITIAL) {
+                throw new IllegalStateException("Cannot start in state:" + stateToString(mState));
+            }
+            try {
+                mPrintDocumentAdapter.start();
+                mState = STATE_STARTED;
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error calling start()", re);
+                mState = STATE_FAILED;
+            }
         }
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 5525774..cd30e26 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -207,6 +207,7 @@
             PrinterInfo favoritePrinter = favoritePrinters.get(i).first;
             if (!alreadyAddedPrinter.contains(favoritePrinter.getId())) {
                 updateAndAddPrinter(printers, favoritePrinter, discoveredPrinters);
+                alreadyAddedPrinter.add(favoritePrinter.getId());
             }
         }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 9c1cf64..811adda 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -1964,7 +1964,7 @@
                                 && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE)
                                 || (mPrinter.getCapabilities() == null
                                 && printer.getCapabilities() != null);
-                mPrinter.copyFrom(printer);
+                mPrinter = printer;
             }
 
             if (available) {
@@ -2394,7 +2394,7 @@
 
             mPrinterAvailabilityDetector.updatePrinter(newPrinterState);
 
-            oldPrinterState.copyFrom(newPrinterState);
+            mCurrentPrinter = newPrinterState;
 
             if ((isActive && gotCapab) || (becameActive && hasCapab)) {
                 if (hasCapab && capabChanged) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index cdfc7ee..13105aa 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -65,7 +65,9 @@
 import android.widget.ListView;
 import android.widget.SearchView;
 import android.widget.TextView;
+import android.widget.Toast;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.printspooler.R;
 
 import java.util.ArrayList;
@@ -101,7 +103,8 @@
     private AnnounceFilterResult mAnnounceFilterResult;
 
     /** Monitor if new print services get enabled or disabled */
-    private ContentObserver mPrintServicesObserver;
+    private ContentObserver mPrintServicesDisabledObserver;
+    private PackageMonitor mPackageObserver;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -145,6 +148,14 @@
         });
 
         registerForContextMenu(mListView);
+
+        // Display a notification about disabled services if there are disabled services
+        String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
+                Settings.Secure.DISABLED_PRINT_SERVICES);
+        if (!TextUtils.isEmpty(disabledServicesSetting)) {
+            Toast.makeText(this, getString(R.string.print_services_disabled_toast),
+                    Toast.LENGTH_LONG).show();
+        }
     }
 
     @Override
@@ -245,28 +256,45 @@
      * Register listener for changes to the enabled print services.
      */
     private void registerServiceMonitor() {
-        mPrintServicesObserver = new ContentObserver(new Handler()) {
+        // Listen for services getting disabled
+        mPrintServicesDisabledObserver = new ContentObserver(new Handler()) {
             @Override
             public void onChange(boolean selfChange) {
                 onPrintServicesUpdate();
             }
         };
 
+        // Listen for services getting installed or uninstalled
+        mPackageObserver = new PackageMonitor() {
+            @Override
+            public void onPackageModified(String packageName) {
+                onPrintServicesUpdate();
+            }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                onPrintServicesUpdate();
+            }
+
+            @Override
+            public void onPackageAdded(String packageName, int uid) {
+                onPrintServicesUpdate();
+            }
+        };
+
         getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.ENABLED_PRINT_SERVICES), false,
-                mPrintServicesObserver);
+                Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false,
+                mPrintServicesDisabledObserver);
+
+        mPackageObserver.register(this, getMainLooper(), false);
     }
 
     /**
-     * Unregister {@link #mPrintServicesObserver listener for changes to the enabled print services}
-     * or nothing if the listener is not registered.
+     * Unregister the listeners for changes to the enabled print services.
      */
     private void unregisterServiceMonitorIfNeeded() {
-        if (mPrintServicesObserver != null) {
-            getContentResolver().unregisterContentObserver(mPrintServicesObserver);
-
-            mPrintServicesObserver = null;
-        }
+        getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver);
+        mPackageObserver.unregister();
     }
 
     @Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
index dd10567..f781159 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/ApprovedPrintServices.java
@@ -23,6 +23,7 @@
 import android.printservice.PrintService;
 import android.util.ArraySet;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -126,29 +127,32 @@
     }
 
     /**
-     * If a {@link PrintService} is approved, remove it from the list of approved services.
+     * Remove all approved {@link PrintService print services} that are not in the given set.
      *
-     * @param serviceToRemove The {@link ComponentName} of the {@link PrintService} to be removed
+     * @param serviceNamesToKeep The {@link ComponentName names } of the services to keep
      */
-    public void removeApprovedService(ComponentName serviceToRemove) {
+    public void pruneApprovedServices(List<ComponentName> serviceNamesToKeep) {
         synchronized (sLock) {
-            if (isApprovedService(serviceToRemove)) {
-                // Copy approved services.
-                ArraySet<String> approvedServices = new ArraySet<String>(
-                        mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null));
+            Set<String> approvedServices = getApprovedServices();
 
+            if (approvedServices == null) {
+                return;
+            }
+
+            Set<String> newApprovedServices = new ArraySet<>(approvedServices.size());
+
+            final int numServiceNamesToKeep = serviceNamesToKeep.size();
+            for(int i = 0; i < numServiceNamesToKeep; i++) {
+                String serviceToKeep = serviceNamesToKeep.get(i).flattenToShortString();
+                if (approvedServices.contains(serviceToKeep)) {
+                    newApprovedServices.add(serviceToKeep);
+                }
+            }
+
+            if (approvedServices.size() != newApprovedServices.size()) {
                 SharedPreferences.Editor editor = mPreferences.edit();
 
-                final int numApprovedServices = approvedServices.size();
-                for (int i = 0; i < numApprovedServices; i++) {
-                    if (approvedServices.valueAt(i)
-                            .equals(serviceToRemove.flattenToShortString())) {
-                        approvedServices.removeAt(i);
-                        break;
-                    }
-                }
-
-                editor.putStringSet(APPROVED_SERVICES_PREFERENCE, approvedServices);
+                editor.putStringSet(APPROVED_SERVICES_PREFERENCE, newApprovedServices);
                 editor.apply();
             }
         }
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index c860668..2189b55 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -3,9 +3,22 @@
 
 LOCAL_MODULE := SettingsLib
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-recyclerview \
+    android-support-v7-preference \
+    android-support-v7-appcompat \
+    android-support-v14-preference
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
+    frameworks/support/v7/preference/res \
+    frameworks/support/v14/preference/res \
+    frameworks/support/v7/appcompat/res \
+    frameworks/support/v7/recyclerview/res
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+    --extra-packages android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat:android.support.v7.recyclerview
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
new file mode 100644
index 0000000..b3d7cf9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="21dp"
+        android:height="21dp"
+        android:viewportWidth="21.0"
+        android:viewportHeight="21.0"
+        android:tint="?android:attr/colorAccent">
+    <path
+            android:fillColor="@android:color/white"
+            android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" />
+</vector>
diff --git a/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml
new file mode 100644
index 0000000..f7a9c9f
--- /dev/null
+++ b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/text1"
+        style="?android:attr/spinnerDropDownItemStyle"
+        android:singleLine="true"
+        android:layout_width="wrap_content"
+        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:ellipsize="marquee" />
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 0d9d79c..30a3b59 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Laat enige program na ekstern geskryf word, ongeag manifeswaardes"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Verplig verstelbare groottes vir aktiwiteite"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Maak grootte van alle aktiwiteite verstelbaar vir veelvuldige vensters, ongeag manifeswaardes."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktiveer vormvrye-Windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiveer steun vir eksperimentele vormvrye-Windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Werkskerm-rugsteunwagwoord"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Volle rekenaarrugsteune word nie tans beskerm nie"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Raak om die wagwoord vir volledige rekenaarrugsteune te verander of te verwyder"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Outomaties"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stel WebView-implementering"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Skakel om na lêerenkripsie"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Skakel om …"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Lêerenkripsie is reeds uitgevoer"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kleurregstelling"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierdie kenmerk is eksperimenteel en kan werkverrigting beïnvloed."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – sowat <xliff:g id="TIME">%2$s</xliff:g> oor"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol op WS"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol oor USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol vanaf draadloos"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Laai"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laai tans op WS"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laai tans oor USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laai tans draadloos"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Laai nie"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laai nie"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 1c9b0a9..beac0a1 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"አንጸባራቂ እሴቶች ግምት ውስጥ ሳይገቡ ማንኛውም መተግበሪያ ወደ ውጫዊ ማከማቻ ለመጻፍ ብቁ ያደርጋል።"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"እንቅስቃሴዎች ዳግመኛ እንዲመጣጠኑ አስገድድ"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"የዝርዝር ሰነድ እሴቶች ምንም ይሁኑ ምን ለበርካታ መስኮቶች ሁሉንም እንቅስቃሴዎች ዳግም የሚመጣጠኑ እንዲሆኑ ያደርጋቸዋል።"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"የነጻ ቅርጽ መስኮቶችን ያንቁ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"የሙከራ ነጻ ቅርጽ መስኮቶች ድጋፍን ያነቃል።"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"የዴስክቶፕ መጠባበቂያ ይለፍ ቃል"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ዴስክቶፕ ሙሉ ምትኬዎች በአሁኑ ሰዓት አልተጠበቁም"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ለዴስክቶፕ ሙሉ ምትኬዎች የይለፍ ቃል ለመለወጥ ወይም ለማስወገድ ንካ"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ራስ-ሰር"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"የWebView ትግበራ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"የWebView ትግበራን ያዘጋጁ"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"የተመረጠው WebView ትግበራ ተሰናክሏል፣ እና ጥቅም ላይ እንዲውል መንቃት አለበት፣ ሊያነቁት ይፈልጋሉ?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ወደ ፋይል ምሥጠራ ቀይር"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ለውጥ…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ፋይል አስቀድሞ ተመስጥሯል"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"የቀለም ማስተካከያ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ይህ ባህሪ የሙከራ ነውና አፈጻጸም ላይ ተጽዕኖ ሊኖረው ይችላል።"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ገደማ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> እስከሚሞላ ድረስ"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በኤሲ ላይ እስከሚሞላ ድረስ"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በዩኤስቢ ላይ እስከሚሞላ ድረስ"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በገመድ አልባ ላይ እስከሚሞላ ድረስ"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"ያልታወቀ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ኃይል በመሙላት ላይ"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"በኤሲ ሃይል በመሙላት ላይ"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"በዩኤስቢ ሃይል በመሙላት ላይ"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"በገመድ አልባ ሃይል በመሙላት ላይ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ባትሪ እየሞላ አይደለም"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ኃይል  እየሞላ አይደለም"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index fc11a6b..2386cb6 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"تأهيل أي تطبيق بحيث تتم كتابته على سعة تخزين خارجية، بغض النظر عن قيم البيان"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"فرض إمكانية تغيير على الأنشطة"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"لتمكين تغيير حجم جميع الأنشطة لتناسب تعدد النوافذ، بغض النظر عن قيم البيان."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"تمكين النوافذ الحرة"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"لتمكين إتاحة استخدام النوافذ الحرة التجريبية."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"كلمة مرور احتياطية للكمبيوتر"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"النسخ الاحتياطية الكاملة لسطح المكتب غير محمية في الوقت الحالي"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"المس لتغيير كلمة مرور النسخ الاحتياطية الكاملة لسطح المكتب أو إزالتها"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"تلقائيًا"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"‏تطبيق WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"‏تعيين تطبيق WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"‏إن تنفيذ ميزة WebView التي تم اختيارها معطَّل، ويجب تمكين هذه الميزة ليتسنى استخدامها، فهل تريد تمكينها؟"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"التحويل إلى تشفير ملفات"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تحويل…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"تم استخدام تشفير ملفات من قبل"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"تصحيح الألوان"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"هذه الميزة تجريبية وقد تؤثر في الأداء."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تبقى <xliff:g id="TIME">%2$s</xliff:g> تقريبًا"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال باستخدام التيار المتردد"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"‏<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال عبر USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال بالشحن اللاسلكي"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"غير معروف"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"شحن"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"جارٍ الشحن بتيار متردد"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏جارٍ الشحن عبر USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"جارٍ الشحن لاسلكيًا"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"لا يتم الشحن"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 4a5ea37..273ea30 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Seçilmiş hər hansı tətbiqi bəyannamə dəyərlərindən aslı olmayaraq xarici yaddaşa yazılabilən edir."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Ölçü dəyişdirmək üçün məcburi fəaliyyətlər"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Bəyannamə dəyərlərindən aslı olmayaraq bütün fəaliyyətləri çoxsaylı pəncərə üçün dəyişkən ölçülü edir."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Freeform windows aktiv edin"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Sınaq üçün freeform windows aktiv edir"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Masaüstü rezerv parolu"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Masaüstü tam rezervlər hazırda qorunmayıblar."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Masaüstünün tam rezevr kopyalanması üçün parolu dəyişmək və ya silmək üçün toxunun"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Avtomatik"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView icrası"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView icrasını ayarlayın"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Seçilmiş WebView icrası deaktiv edildi, istifadəsi üçün aktiv edilməlidir, aktivləşdirmək istəyirsiniz?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Fayl şifrələnməsinə çevirin"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Çevirin..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl artıq şifrələnib"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Rəng düzəlişi"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya eksperimentaldır və performansa təsir edə bilər."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - təxminən <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> dolana qədər"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC üzərindən dolana qədər"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB üzərindən dolana qədər"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> naqilsiz üzərindən dolana qədər"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Naməlum"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Enerji doldurma"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Dəyişən cərəyanda qidalanır"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB üzərindən qidalanır"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Naqilsiz qidalanır"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Doldurulmur"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Enerji doldurulmur"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 676c618..5d8b934 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Omogućava upisivanje svih aplikacija u spoljnu memoriju, bez obzira na vrednosti manifesta"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Prinudno omogući promenu veličine aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Omogućava promenu veličine svih aktivnosti za režim sa više prozora, bez obzira na vrednosti manifesta."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Omogući prozore proizvoljnog formata"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Omogućava podršku za eksperimentalne prozore proizvoljnog formata."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Lozinka rezervne kopije za računar"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Rezervne kopije čitavog sistema trenutno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dodirnite da biste promenili ili uklonili lozinku za pravljenje rezervnih kopija čitavog sistema na računaru"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatski"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Primena WebView-a"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Podesite primenu WebView-a"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertuj u šifrovanje datoteka"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertuj..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Već se koristi šifrovanje datoteka"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcija boja"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može da utiče na performanse."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo oko <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni punjačem"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni preko USB-a"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni bežično"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje preko punjača"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje preko USB-a"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 8180261..e5dbf50 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Позволява прилож. да се записват във външ. хранил. независимо от стойностите в манифеста"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Възможност за преоразмеряване на активностите"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Дава възможност за преоразмеряване на всички активности в режима за няколко прозореца независимо от стойностите в манифеста."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Активиране на прозорците в свободна форма"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Активира поддръжката за експерименталните прозорци в свободна форма."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Наст. комп.: Парола"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Понастоящем пълните резервни копия за настолен компютър не са защитени"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Докоснете, за да промените или премахнете паролата за пълни резервни копия на настолния компютър"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Автоматично"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Внедряване на WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Задаване на внедряването на WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Преобразуване към шифроване на ниво файл"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Преобразуване…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Данните вече са шифровани на ниво файл"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекция на цветове"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Тази функция е експериментална и може да се отрази на ефективността."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – приблизително оставащо време: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане при променлив ток"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> ˜– <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане през USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно безжично зареждане"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарежда се"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зареждане при AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зареждане през USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично зареждане"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Не се зарежда"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се зарежда"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index a5787f5..9e42f71 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ম্যানিফেস্ট মানগুলি নির্বিশেষে যেকোনো অ্যাপ্লিকেশানকে বাহ্যিক সঞ্চয়স্থানে লেখার উপযুক্ত বানায়"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"আকার পরিবর্তনযোগ্য করার জন্য ক্রিয়াকলাপগুলিকে জোর করুন"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ম্যানিফেস্ট মানগুলির নির্বিশেষে মাল্টি-উইন্ডোর জন্য সমস্ত ক্রিয়াকলাপগুলিকে আকার পরিবর্তনযোগ্য করে তোলে৷"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ফ্রি-ফর্ম উইন্ডোগুলি সক্ষম করুন"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"পরীক্ষামূলক ফ্রি-ফর্ম উইন্ডোগুলির জন্য সহায়তা সক্ষম করুন৷"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ডেস্কটপ ব্যাকআপ পাসওয়ার্ড"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ডেস্কটপ পূর্ণ ব্যাকআপ বর্তমানে সুরক্ষিত নয়"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ডেস্কটপ পুরো ব্যাকআপের জন্য পাসওয়ার্ড পরিবর্তন বা মুছে ফেলার জন্য স্পর্শ করুন"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"স্বয়ংক্রিয়"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"ওয়েবভিউ প্রয়োগ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ওয়েবভিউ প্রয়োগ সেট করুন"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"নির্বাচিত ওয়েবভিউ প্রয়োগটি অক্ষম করা আছে এবং ব্যবহার করার জন্য অবশ্যই সক্ষম করতে হবে, আপনি কি এটিকে সক্ষম করতে চান?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ফাইল এনক্রিপশান রূপান্তর করুন"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"রূপান্তর করুন..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ফাইল ইতিমধ্যেই এনক্রিপ্ট করা রয়েছে"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"রঙ সংশোধন"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই বৈশিষ্ট্যটি পরীক্ষামূলক এবং এটি কার্য-সম্পাদনা প্রভাবিত করতে পারে।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - আনুমানিক <xliff:g id="TIME">%2$s</xliff:g> বাকি আছে"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACতে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB এর মাধ্যমে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> -  বেতার যোগে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"অজানা"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC তে চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB এর মাধ্যমে চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"তারবিহীনভাবে চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"চার্জ হচ্ছে না"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"চার্জ হচ্ছে না"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 8553ed9..2f81098 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Permet que les aplicacions es puguin escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Força l\'ajust de la mida de les activitats"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet ajustar la mida de totes les activitats per al mode multifinestra, independentment dels valors definits."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activa les finestres de format lliure"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Activa la compatibilitat amb les finestres de format lliure experimentals."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Contrasenya per a còpies d\'ordinador"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les còpies de seguretat d\'ordinador completes no estan protegides"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca per canviar o eliminar la contrasenya per a còpies de seguretat d\'ordinador completes"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automàtic"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementació de WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configura la implementació de WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"La implementació de WebView que has triat està desactivada i s\'ha d\'activar per utilitzar-la. Vols activar-la?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converteix en l\'encriptació de fitxers"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converteix…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"El fitxer ja està encriptat"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correcció del color"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Aquesta funció és experimental i pot afectar el rendiment."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: falten aproximadament <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega sense fil"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Càrrega: corr. alt."</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Càrrega per USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Càrrega sense fils"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"No s\'està carregant"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No s\'està carregant"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 495f10f..f1c9e27 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Každou aplikaci bude možné zapsat do externího úložiště, bez ohledu na hodnoty manifestu"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Vynutit možnost změny velikosti aktivit"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Velikost všech aktivit bude možné změnit na několik oken (bez ohledu na hodnoty manifestu)."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivovat okna s volným tvarem"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktivuje podporu experimentálních oken s volným tvarem."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Heslo pro zálohy v počítači"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Úplné zálohy v počítači nejsou v současné době chráněny"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tuto možnost vyberte, chcete-li změnit nebo odebrat heslo pro úplné zálohy v počítači"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatický"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementace WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavte implementaci WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Vybraná implementace WebView je zakázána a nelze ji použít. Chcete ji povolit a použít?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Převést na šifrování souborů"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Převést…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Obsah je již na úrovni souborů zašifrován"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekce barev"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkce je experimentální a může mít vliv na výkon."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá přibližně <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití ze zásuvky"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití přes USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití bezdrátově"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznámé"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjí se"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjení z adaptéru"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjení přes USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrátové nabíjení"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíjí se"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíjí se"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 82654bc..90ea69a 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gør det muligt at overføre enhver app til et eksternt lager uafhængigt af manifestværdier"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Tving aktiviteter til at kunne tilpasses"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Sørger for, at alle aktiviteter kan tilpasses flere vinduer uafhængigt af manifestværdier."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivér vinduer i frit format"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiverer understøttelse af eksperimentelle vinduer i frit format."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Kode til lokal sikkerhedskopi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Lokale fuldstændige sikkerhedskopieringer er i øjeblikket ikke beskyttet"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tryk for at skifte eller fjerne adgangskoden til fuld lokal sikkerhedskopiering"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatisk"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Konfigurer WebView-implementering"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Den valgte WebView-implementering er deaktiveret og skal aktiveres, før den kan bruges. Vil du aktivere den?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertér til filkryptering"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede filkrypteret"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korriger farver"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funktion er eksperimentel og kan påvirke ydeevnen."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med adapter"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med trådløs"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukendt"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Oplader"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladning med AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladning via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Trådløs opladning"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Oplader ikke"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Oplader ikke"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Fuld"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index a2f750b..8d95ddf 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Ermöglicht es jeder qualifizierten App, Daten auf externen Speicher zu schreiben, unabhängig von den Manifestwerten"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Anpassen der Größe von Aktivitäten erzwingen"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Ermöglicht es, die Größe aller Aktivitäten an den Mehrfenstermodus anzupassen, unabhängig von den Manifestwerten."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Freiform-Fenster zulassen"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Unterstützt experimentelle Freiform-Fenster."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop-Sicherungspasswort"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Vollständige Desktop-Sicherungen sind momentan nicht passwortgeschützt."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Zum Ändern oder Entfernen des Passworts für vollständige Desktop-Sicherungen berühren"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatisch"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-Implementierung"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-Implementierung festlegen"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Zu Dateiverschlüsselung wechseln"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Wechseln…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dateiverschlüsselung wird bereits verwendet."</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Farbkorrektur"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierbei handelt es sich um eine experimentelle Funktion. Dies kann sich auf die Leistung auswirken."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noch etwa <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei Stromanschluss voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – über USB voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei kabellosem Laden voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Unbekannt"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Wird aufgeladen"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laden über Netzteil"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laden über USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kabelloses Laden"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Wird nicht geladen"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wird nicht geladen"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index d426616..303f0e5 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Κάνει κάθε εφαρμογή κατάλληλη για εγγραφή σε εξωτερικό χώρο αποθήκευσης, ανεξάρτητα από τις τιμές του μανιφέστου"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Αναγκαστική δυνατότητα αλλαγής μεγέθους δραστηριοτήτων"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Να έχουν όλες οι δραστηριότητες δυνατότητα αλλαγής μεγέθους για την προβολή πολλαπλών παραθύρων, ανεξάρτητα από τις τιμές του μανιφέστου."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ενεργοποίηση παραθύρων ελεύθερης μορφής"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ενεργοποιεί την υποστήριξη για πειραματικά παράθυρα ελεύθερης μορφής."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Εφ/κός κωδικός desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Τα πλήρη αντίγραφα ασφαλείας επιφάνειας εργασίας δεν προστατεύονται αυτήν τη στιγμή"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Αγγίξτε για αλλαγή ή κατάργηση του κωδικού πρόσβασης για τα πλήρη αντίγραφα ασφαλείας επιφάνειας εργασίας"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Αυτόματο"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Υλοποίηση WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ορισμός υλοποίησης WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Μετατροπή σε κρυπτογράφηση αρχείου"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Μετατροπή…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Με κρυπτογράφηση αρχείου"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Διόρθωση χρωμάτων"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Αυτή η λειτουργία είναι πειραματική και ενδεχομένως να επηρεάσει τις επιδόσεις."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - απομένουν περίπου <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση με φορτιστή AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση μέσω USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη ασύρματη φόρτιση"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Άγνωστο"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Φόρτιση"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Φόρτιση με AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Φόρτιση μέσω USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ασύρματη φόρτιση"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Δεν φορτίζει"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Δεν φορτίζει"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b8c8521..b938ace 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Enable freeform windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Enables support for experimental freeform windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"The chosen WebView implementation is disabled and must be enabled to be used, do you wish to enable it?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Colour correction"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b8c8521..b938ace 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Enable freeform windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Enables support for experimental freeform windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"The chosen WebView implementation is disabled and must be enabled to be used, do you wish to enable it?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Colour correction"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b8c8521..b938ace 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Makes any app eligible to be written to external storage, regardless of manifest values"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Force activities to be re-sizable"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Makes all activities re-sizable for multi-window, regardless of manifest values."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Enable freeform windows"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Enables support for experimental freeform windows."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop backup password"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Desktop full backups aren\'t currently protected"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Touch to change or remove the password for desktop full backups"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"The chosen WebView implementation is disabled and must be enabled to be used, do you wish to enable it?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Colour correction"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 34ebef4..9ddc03f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Cualquier aplicación puede escribirse en una memoria externa, independientemente de los valores del manifiesto."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar actividades para que cambien de tamaño"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite que todas las actividades puedan cambiar de tamaño para el modo multiventana, sin importar los valores del manifiesto."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Habilitar ventanas de forma libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Habilita la admisión de ventanas de forma libre experimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Contraseñas"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Tus copias de seguridad de escritorio no están protegidas por contraseña."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca para cambiar o eliminar la contraseña de las copias de seguridad completas de tu escritorio."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar la implementación de WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir a encriptación de archivo"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ya está encriptado"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección de color"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar el rendimiento."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: alrededor de <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga inalámbrica"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carga en CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carga con USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carga inalámbrica"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando."</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se realiza la carga"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Cargado"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index cedd582..f0682c2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Hace que cualquier aplicación se pueda escribir en un dispositivo de almacenamiento externo, independientemente de los valores definidos"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar el ajuste de tamaño de las actividades"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite que se pueda ajustar el tamaño de todas las actividades para el modo multiventana, independientemente de los valores establecidos."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Habilitar ventanas de forma libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Permite utilizar ventanas de forma libre experimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Contraseña para copias de ordenador"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Las copias de seguridad completas de ordenador no están protegidas"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tocar para cambiar o quitar la contraseña para las copias de seguridad completas de ordenador"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Establecer implementación de WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir a cifrado de archivo"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ya está cifrado"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección del color"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar al rendimiento."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quedan <xliff:g id="TIME">%2$s</xliff:g> aproximadamente"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería por USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con Wi-Fi"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando en CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando de forma inalámbrica"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se está cargando"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 714004e..ff7d7ee 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Lubab rakendusi kirjutada välisesse salvestusruumi olenemata manifesti väärtustest"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Muuda tegevuste suurused muudetavaks"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Muudab kõigi tegevuste suurused mitme aknaga vaates olenemata manifesti väärtustest muudetavaks."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Luba vabas vormis aknad"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Lubatakse katseliste vabas vormis akende tugi."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Arvutivarunduse parool"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Täielikud arvutivarundused pole praegu kaitstud"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Puudutage, et muuta või eemaldada täielike arvutivarunduste parool"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automaatne"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView\' rakendamine"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView\' rakendamise seadistamine"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Teisendamine failikrüpteeringuga andmeteks"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Teisenda …"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Juba failikrüpteeringuga"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Värviparandus"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"See funktsioon on katseline ja võib mõjutada toimivust."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – jäänud on umbes <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (vahelduvvool)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (USB)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (juhtmeta laad.)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Tundmatu"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Laadimine"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laad. vahelduvv.-v."</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laadimine USB kaudu"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Juhtmevaba laadimine"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ei lae"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei lae"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index 7f47bf3..145e8f8 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Aplikazioek kanpoko memorian idatz dezakete, manifestuaren balioak kontuan izan gabe"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Behartu jardueren tamaina doitu ahal izatea"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifestuan jartzen duena jartzen duela ere, jarduera guztien tamaina doitzeko aukera ematen du, hainbat leihotan erabili ahal izan daitezen."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Gaitu estilo libreko leihoak"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Estilo libreko leiho esperimentalak onartzen ditu."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Tokiko babeskop. pasahitza"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Une honetan, ordenagailuko babeskopia osoak ez daude babestuta."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Ukitu ordenagailuko babeskopia osoak egiteko pasahitza aldatzeko edo kentzeko"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatikoa"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Desgaituta dago aukeratu den Web-ikustailearen inplementazioa. Erabili nahi izanez gero, gaitu egin behar duzu. Gaitu nahi al duzu?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Eman fitxategietan oinarritutako enkriptatzea"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Enkriptatu…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fitxategietan oinarritutako enkriptatzea dauka dagoeneko"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kolore-zuzenketa"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Eginbidea esperimentala da eta eragina izan dezake funtzionamenduan."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> inguru. <xliff:g id="TIME">%2$s</xliff:g> geratzen d(ir)a"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> korrontearen bidez guztiz kargatu arte"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB bidez guztiz kargatu arte"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> haririk gabe guztiz kargatu arte"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Ezezaguna"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Kargatzea"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"KA bidez kargatzen"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB bidez kargatzen"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hari gabe kargatzen"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ez da kargatzen ari"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ez da kargatzen ari"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1f8b575..5380651 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"بدون توجه به مقادیر مانیفست، هر برنامه‌ای را برای نوشتن در حافظه خارجی واجد شرایط می‌کند"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"اجبار فعالیت‌ها به قابل تغییر اندازه بودن"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"بدون درنظر گرفتن مقادیر مانیفست، همه فعالیت‌ها را برای چندپنجره قابل تغییر اندازه می‌کند."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"فعال کردن پنجره‌های آزاد"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"پشتیبانی برای پنجره‌های آزاد آزمایشی را امکان‌پذیر می‌کند"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"گذرواژه پشتیبان‌گیری محلی"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"پشتیبان‌گیری کامل رایانه درحال حاضر محافظت نمی‌شود"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"برای تغییر یا حذف گذرواژه برای نسخه‌های پشتیبان کامل دسک‌تاپ لمس کنید"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"خودکار"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"‏اجرای WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"‏تنظیم اجرای WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"‏پیاده‌سازی WebView انتخاب‌شده غیرفعال شده است و برای استفاده شدن باید فعال شود؛ می‌خواهید آن را فعال کنید؟"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"تبدیل به رمزگذاری برحسب فایل"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تبدیل…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"از قبل به رمزگذاری بر حسب فایل تبدیل شده است"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"تصحیح رنگ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"این قابلیت آزمایشی است و ممکن است عملکرد را تحت تأثیر قرار دهد."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریباً ‏<xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل با جریان متناوب"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"‏<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل از طریق USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل به‌طور بی‌سیم"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"ناشناس"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"در حال شارژ شدن"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"شارژ با جریان متناوب"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏شارژ از طریق USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"شارژ به صورت بی‌سیم"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"شارژ نمی‌شود"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"شارژ نمی‌شود"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index e4b4b93..f9fdb63 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Mahdollistaa sovellusten tallentamisen ulkoiseen tall.tilaan luettelosta riippumatta"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Pakota kaikki toiminnot hyväksymään koon muutos"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Pakottaa kaikki toiminnot hyväksymään koon muuttamisen rinnakkaisnäkymään luettelon arvoista riippumatta."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ota käyttöön vapaamuotoiset ikkunat"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ottaa käyttöön kokeellisten vapaamuotoisten ikkunoiden tuen."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Varmuuskop. salasana"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Tietokoneen kaikkien tietojen varmuuskopiointia ei ole tällä hetkellä suojattu"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Muuta tai vaihda tietokoneen kaikkien tietojen varmuuskopioinnin salasana koskettamalla"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automaattinen"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-käyttöönotto"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Määritä WebView-käyttöönotto"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Muunna tiedostojen salaukseksi"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Muunna…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Tiedostot on jo salattu."</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Värikorjaus"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tämä ominaisuus on kokeellinen ja voi vaikuttaa suorituskykyyn."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noin <xliff:g id="TIME">%2$s</xliff:g> jäljellä"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (laturilataus)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (USB-lataus)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (WiFi-lataus)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Tuntematon"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Ladataan"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laturilataus"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-lataus"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Langaton lataus"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ei laturissa"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei laturissa"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 66c54a1..9f802ab 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Permet enreg. d\'applis sur espace stockage externe"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forcer les activités à être redimensionnables"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet de redimensionner toutes les activités pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activer les fenêtres de forme libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Active la compatibilité avec les fenêtres de forme libre expérimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Mot de passe sauvegarde PC"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Appuyez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur PC."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatique"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Mise en œuvre WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Définir la mise en œuvre WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir en chiffrement basé sur un fichier"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Déjà chiffré par un fichier"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correction des couleurs"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut toucher les performances."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> %% – Temps restant : environ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (charge complète sur c.a. dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% par USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% avec chargeur sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge (c.a.)"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge par USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"N\'est pas en charge"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"N\'est pas en charge"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Pleine"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index e56940a..6dabba8 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Rend possible enregistrement de toute appli sur espace stockage externe, indépendamment valeurs fichier manifeste."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forcer possibilité de redimensionner les activités"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permet de redimensionner toutes les activités pour le mode multifenêtre, indépendamment des valeurs du fichier manifeste."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activer les fenêtres de forme libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Active la compatibilité avec les fenêtres de forme libre expérimentales."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Mot de passe sauvegarde PC"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Les sauvegardes complètes sur PC ne sont pas protégées actuellement."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Appuyez pour modifier ou supprimer le mot de passe utilisé pour les sauvegardes complètes sur PC."</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatique"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Mise en œuvre WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Définir la mise en œuvre WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"La mise en œuvre WebView sélectionnée est désactivée. Vous devez l\'activer pour l\'utiliser. Souhaitez-vous l\'activer ?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir en chiffrement basé sur un fichier"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Déjà chiffré via un fichier"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correction couleur"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut affecter les performances."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Temps restant : <xliff:g id="TIME">%2$s</xliff:g> environ"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sur secteur dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% via USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge sur secteur"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Pas en charge"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Débranchée"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 9bcf770..0f0d13d 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Fai que calquera aplicación se poida escribir nun almacenamento externo, independentemente dos valores expresados"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forzar o axuste do tamaño das actividades"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite axustar o tamaño de todas as actividades para o modo de varias ventás, independentemente dos valores definidos."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activar ventás de forma libre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Activa a compatibilidade con ventás de forma libre experimentais."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Contrasinal para copias"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"As copias de seguridade de ordenador completas non están protexidas"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toca para cambiar ou eliminar o contrasinal para as copias de seguranza completas do escritorio"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Definir implementación de WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter no encriptado baseado en ficheiros"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Xa se encriptou o ficheiro"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección da cor"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función é experimental e pode afectar ao rendemento."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - faltan aproximadamente <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga co modo sen fíos"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando con CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando sen fíos"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Non se está cargando"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non está cargando"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 6dff8fa..9e2ec7e 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"મેનિફેસ્ટ મૂલ્યોને ધ્યાનમાં લીધા સિવાય, કોઈપણ એપ્લિકેશનને બાહ્ય સ્ટોરેજ પર લખાવા માટે લાયક બનાવે છે"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"પ્રવૃત્તિઓને ફરીથી કદ યોગ્ય થવા માટે ફરજ પાડો"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"તમામ પ્રવૃત્તિઓને મલ્ટી-વિંડો માટે ફરીથી કદ બદલી શકે તેવી બનાવે છે, મેનીફેસ્ટ મુલ્યોને ધ્યાનમાં લીધા સિવાય."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ફ્રિફોર્મ વિંડોઝ સક્ષમ કરો"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"પ્રાયોગિક ફ્રિફોર્મ વિંડોઝ માટે સમર્થનને સક્ષમ કરે છે."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ડેસ્કટૉપ બેકઅપ પાસવર્ડ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ હાલમાં સુરક્ષિત નથી"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ડેસ્કટૉપ સંપૂર્ણ બેકઅપ્સ માટેનો પાસવર્ડ બદલવા અથવા દૂર કરવા માટે ટચ કરો"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"સ્વચલિત"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView અમલીકરણ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView અમલીકરણ સેટ કરો"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"પસંદ કરેલ WebView અમલીકરણ અક્ષમ કરેલ છે અને ઉપયોગ કરવા માટે સક્ષમ કરવું આવશ્યક છે, શું તમે તેને સક્ષમ કરવા માગો છો?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ફાઇલ એન્ક્રિપ્શનમાં રૂપાંતરિત કરો"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"રૂપાંતરિત કરો..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ફાઇલ પહેલેથી જ એન્ક્રિપ્ટ કરેલ છે"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"રંગ સુધારણા"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"આ સુવિધા પ્રાયોગિક છે અને કામગીરી પર અસર કરી શકે છે."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - આશરે <xliff:g id="TIME">%2$s</xliff:g> બાકી"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"સંપૂર્ણ થવામાં <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>, AC પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>, USB પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> વાયરલેસ દ્વારા પૂર્ણ થાય ત્યાં સુધી"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"અજાણ્યું"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ચાર્જ થઈ રહ્યું છે"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC પર ચાર્જિંગ"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB થી ચાર્જિંગ"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"વાયરલેસથી ચાર્જિંગ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ચાર્જ થઈ રહ્યું નથી"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ચાર્જ થઈ રહ્યું નથી"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 9ce83fa..b6b8eac 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"इससे कोई भी ऐप मेनिफेस्‍ट मान अनदेखा करके, बाहरी मेमोरी पर लिखने योग्‍य बन जाता है"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"आकार बदले जाने के लिए गतिविधियों को बाध्य करें"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"एकाधिक-विंडो के लिए सभी गतिविधियों के आकार को बदले जाने योग्य बनाता है, चाहे मेनिफेस्ट मान कुछ भी हों."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"फ़्रीफ़ॉर्म विंडो सक्षम करें"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"प्रयोगात्मक फ़्रीफ़ॉर्म विंडो का समर्थन सक्षम करती है."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्‍कटॉप बैकअप पासवर्ड"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्‍कटॉप पूर्ण बैकअप वर्तमान में सुरक्षित नहीं हैं"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटॉप के पूर्ण बैकअप के पासवर्ड को बदलने या निकालने के लिए स्‍पर्श करें."</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"स्वचालित"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट करें"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"चुना गया WebView कार्यान्वयन अक्षम है और उसे उपयोग करने के लिए सक्षम किया जाना आवश्यक है, क्या आप उसे सक्षम करना चाहते हैं?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"फ़ाइल एन्क्रिप्शन में रूपांतरित करें"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करें..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फ़ाइल पहले से एन्क्रिप्ट की हुई है"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"रंग सुधार"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यह सुविधा प्रायोगिक है और निष्पादन को प्रभावित कर सकती है."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग <xliff:g id="TIME">%2$s</xliff:g> शेष"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूरी होने तक"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC पर पूरी होने तक"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB पर पूरी होने तक"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेस से पूरी होने तक"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हो रही है"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC से चार्ज हो रही"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB पर चार्ज हो रही"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस रूप से चार्ज हो रही"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज नहीं हो रही है"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज नहीं हो रही है"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index dd02171..0ab13f6 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Aplikacije se mogu zapisivati u vanjsku pohranu neovisno o manifestu"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Nametni mogućnost promjene veličine za aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Veličina svih aktivnosti može se mijenjati za više prozora, neovisno o vrijednostima manifesta."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Omogući prozore slobodnog oblika"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Omogućuje podršku za eksperimentalne prozore slobodnog oblika."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Zaporka sigurnosne kopije"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Potpune sigurnosne kopije na stolnom računalu trenutačno nisu zaštićene"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Odaberite za promjenu ili uklanjanje zaporke u potpunim sigurnosnim kopijama na stolnom računalu"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatska"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementacija WebViewa"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Postavi implementaciju WebViewa"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Pretvori u enkripciju datoteka"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pretvori…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Enkripcija datoteka već je izvršena"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcija boje"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova je značajka eksperimentalna i može utjecati na performanse."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – još približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti strujnim napajanjem"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti putem USB-a"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti bežičnim putem"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje punjačem"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje putem USB-a"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 9b3a386..26d65fb 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Lehetővé teszi, hogy külső tárhelyre lehessen írni"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Tevékenységek átméretezésének kényszerítése"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Lehetővé teszi, hogy az összes tevékenység átméretezhető legyen a többablakos megjelenítés érdekében a jegyzékértékektől függetlenül."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Szabad formájú ablakok engedélyezése"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Engedélyezi a kísérleti jellegű, szabad formájú ablakok támogatását."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Asztali mentés jelszava"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Az asztali teljes biztonsági mentések jelenleg nem védettek."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Érintse meg, ha módosítaná vagy eltávolítaná a jelszót az asztali teljes mentésekhez"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatikus"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-megvalósítás"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-megvalósítás beállítása"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"A kiválasztott WebView-megvalósítás le van tiltva, a használathoz viszont engedélyezni kell. Szeretné engedélyezni?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertálás fájlalapú titkosításra"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertálás…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Már fájlalapú titkosítást használ"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Színkorrekció"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ez egy kísérleti funkció, és hatással lehet a teljesítményre."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – kb. <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttség eléréséig"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig hálózatról"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig USB-ről"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig vezeték nélkül"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Ismeretlen"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Töltés"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hálózati töltés"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-s töltés"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Nem vezetékes töltés"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nem tölt"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nem töltődik"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Feltöltve"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 27db9fe..7a9b16f 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Թույլ է տալիս պահել հավելվածը արտաքին սարքում՝ մանիֆեստի արժեքներից անկախ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Ստիպել, որ ակտիվությունների չափերը լինեն փոփոխելի"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Բոլոր ակտիվությունների չափերը բազմապատուհան ռեժիմի համար դարձնել փոփոխելի՝ մանիֆեստի արժեքներից անկախ:"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ակտիվացնել կամայական ձևի պատուհանները"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ակտիվացնում է կամայական ձևի փորձնական պատուհանների աջակցումը:"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Աշխատասեղանի պահուստավորման գաղտնաբառ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Աշխատասեղանի ամբողջական պահուստավորումները այժմ պաշտպանված չեն"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Աշխատասեղանի ամբողջական պահուստավորման համար ընտրել փոխել կամ հեռացնել գաղտնաբառը"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Ավտոմատ"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-ի իրականացում"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ընտրեք WebView-ի իրականացումը"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Վերածել ֆայլային գաղտնագրման"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Փոխարկել…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ֆայլային գաղտնագրումն արդեն կատարվել է"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Գունային կարգաբերում"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Սա փորձնական գործառույթ է և կարող է ազդել աշխատանքի վրա:"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - մնաց մոտավորապես <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը հոսանքից"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը USB-ով"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը անլար ցանցից"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Անհայտ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Լիցքավորում"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Լիցքավորում AC-ով"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Լիցքավորում USB-ով"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Անլար լիցքավորում"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Չի լիցքավորվում"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Չի լիցքավորվում"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 0cea206..f2eaccf 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksterna"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Paksa aktivitas agar ukurannya dapat diubah"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Membuat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktifkan jendela berformat bebas"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Mengaktifkan dukungan untuk jendela eksperimental berformat bebas."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Sandi cadangan desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Saat ini cadangan desktop penuh tidak dilindungi"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Sentuh guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Otomatis"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Penerapan WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Setel penerapan WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konversi ke enkripsi file"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konversi..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sudah dienkripsi berbasis file"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Koreksi warna"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Fitur ini bersifat eksperimental dan dapat memengaruhi kinerja."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira tersisa. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh pada AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh melalui USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh dari nirkabel"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengisi daya"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengisi daya pada AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Isi daya lewat USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Isi daya nirkabel"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengisi daya"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengisi daya"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index b3509ca..dc105eb 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gerir hvaða forriti sem er kleift að skrifa í ytri geymslu, burtséð frá gildum í upplýsingaskrá"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Þvinga breytanlega stærð virkni"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Gerir stærð allrar virkni breytanlega svo að hún henti fyrir marga glugga, óháð gildum í upplýsingaskrá."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Virkja glugga með frjálsu sniði"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Kveikir á stuðningi við glugga með frjálsu sniði á tilraunastigi."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Aðgangsorð tölvuafritunar"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Heildarafritun á tölvu er ekki varin sem stendur."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Snertu til að breyta eða fjarlægja aðgangsorðið fyrir heildarafritun á tölvu"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Sjálfvirkt"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Innleiðing WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stilla innleiðingu WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Umbreyta í dulkóðun skráa"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Umbreyta…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Þegar dulkóðað á grundvelli skráa"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Litaleiðrétting"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Þessi eiginleiki er á tilraunastigi og getur haft áhrif á frammistöðu."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – u.þ.b. <xliff:g id="TIME">%2$s</xliff:g> eftir"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu með hleðslutæki"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu í gegnum USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu þráðlaust"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Óþekkt"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Í hleðslu"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hleðslutæki tengt"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Hleður um USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hleður þráðlaust"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ekki í hleðslu"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ekki í hleðslu"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 9f897f7..eedfa33 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Rende l\'app idonea all\'installaz. su mem. esterna, senza considerare i valori manifest"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Imponi formato modificabile alle attività"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Rende il formato di tutte le attività modificabile per la modalità multi-finestra, indipendentemente dai valori manifest."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Attiva finestre a forma libera"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Attiva il supporto per le finestre a forma libera sperimentali."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Password di backup desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"I backup desktop completi non sono attualmente protetti."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tocca per modificare o rimuovere la password per i backup desktop completi"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatico"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementazione di WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Imposta l\'implementazione di WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"L\'implementazione di WebView selezionata non è attiva e deve essere attivata per poterla utilizzare. Vuoi attivarla?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converti in crittografia basata su file"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converti..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Crittografia su base file già eseguita"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correzione del colore"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Questa funzione è sperimentale e potrebbe influire sulle prestazioni."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tempo rimanente: <xliff:g id="TIME">%2$s</xliff:g> circa"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lla carica completa con wireless"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Sconosciuta"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"In carica"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"In carica tramite CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"In carica tramite USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"In carica, wireless"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Non in carica"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non in carica"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 2d44612..ec8f1a5 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -190,7 +190,7 @@
     <string name="hdcp_checking_title" msgid="8605478913544273282">"‏בדיקת HDCP"</string>
     <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"‏הגדר אופן בדיקת HDCP"</string>
     <string name="debug_debugging_category" msgid="6781250159513471316">"ניפוי באגים"</string>
-    <string name="debug_app" msgid="8349591734751384446">"בחר אפליקציה לניפוי"</string>
+    <string name="debug_app" msgid="8349591734751384446">"בחר אפליקציה לניפוי באגים"</string>
     <string name="debug_app_not_set" msgid="718752499586403499">"לא הוגדרה אפליקציה לניפוי"</string>
     <string name="debug_app_set" msgid="2063077997870280017">"אפליקציה לניפוי: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="select_application" msgid="5156029161289091703">"בחר אפליקציה"</string>
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"אלץ יכולת קביעת גודל של הפעילויות"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"מאפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא קשר לערך המניפסט."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"הפעל את האפשרות לשנות את הגודל והמיקום של החלונות"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"מפעיל תמיכה בתכונה הניסיונית של שינוי הגודל והמיקום של החלונות."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"סיסמת גיבוי מקומי"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"גע כדי לשנות או להסיר את הסיסמה עבור גיבויים מלאים בשולחן העבודה"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"באופן אוטומטי"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"‏יישום WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"‏הגדרת יישום WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"המר להצפנת קבצים"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"המר..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"הצפנת קבצים כבר מוגדרת"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"תיקון צבע"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"תכונה זו היא ניסיונית ועשויה להשפיע על הביצועים."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> בקירוב עד לסיום"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בזרם חילופין"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"‏<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי ב-USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בטעינה אלחוטית"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"לא ידוע"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"טוען"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"טוען בזרם חילופין"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏טוען ב-USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"טוען באופן אלחוטי"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"לא בטעינה"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"לא טוען"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 81a5eaf..9a32d44 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"マニフェストの値に関係なく、すべてのアプリを外部ストレージに書き込めるようになります"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"アクティビティをサイズ変更可能にする"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"マニフェストの値に関係なく、マルチウィンドウですべてのアクティビティのサイズを変更できるようになります。"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"フリーフォーム ウィンドウの有効化"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"テスト段階のフリーフォーム ウィンドウのサポートを有効にします。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"PCバックアップパスワード"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"デスクトップのフルバックアップは現在保護されていません"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"デスクトップのフルバックアップ用のパスワードを変更または削除する場合にタップします"</string>
@@ -263,9 +265,9 @@
     <item msgid="8280754435979370728">"目に自然な色"</item>
     <item msgid="5363960654009010371">"デジタルコンテンツに最適な色"</item>
   </string-array>
-    <string name="inactive_apps_title" msgid="1317817863508274533">"無効なアプリ"</string>
-    <string name="inactive_app_inactive_summary" msgid="6768756967594202411">"無効。タップすると切り替わります。"</string>
-    <string name="inactive_app_active_summary" msgid="4512911571954375968">"有効。タップすると切り替わります。"</string>
+    <string name="inactive_apps_title" msgid="1317817863508274533">"休止中のアプリ"</string>
+    <string name="inactive_app_inactive_summary" msgid="6768756967594202411">"休止中。タップすると切り替わります。"</string>
+    <string name="inactive_app_active_summary" msgid="4512911571954375968">"実行中。タップすると切り替わります。"</string>
     <string name="runningservices_settings_title" msgid="8097287939865165213">"実行中のサービス"</string>
     <string name="runningservices_settings_summary" msgid="854608995821032748">"現在実行中のサービスを表示して制御する"</string>
     <string name="night_mode_title" msgid="2594133148531256513">"夜間モード"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView の実装"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView の実装の設定"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ファイル暗号化に変換する"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"変換…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ファイルは既に暗号化済みです"</string>
@@ -291,4 +295,20 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色補正"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"この機能は試験運用機能であり、パフォーマンスに影響することがあります。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り約<xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(AC)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(USB)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(ワイヤレス)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACで充電しています"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBで充電しています"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"無線で充電しています"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"充電していません"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"充電していません"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for battery_info_status_full (2824614753861462808) -->
+    <skip />
 </resources>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index e2dfd7e..0cfb6ec 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"აპები ჩაიწერ. გარე მეხს.-ზე აღწ. ფაილის მნიშვნ. მიუხედ."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ზომაცვლადი აქტივობების იძულება"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"მანიფესტის მნიშვნელობების მიუხედავად, ყველა აქტივობას მრავალი ფანჯრის რეჟიმისთვის ზომაცვლადად აქცევს."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"თავისუფალი ფორმის მქონე ფანჯრების ჩართვა"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ჩართავს თავისუფალი ფორმის მქონე ფანჯრების მხარდაჭერის ექსპერიმენტულ ფუნქციას"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"დესკტოპის სარეზერვო ასლის პაროლი"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"დესკტოპის სრული სარეზერვო ასლები ამჟამად დაცული არ არის"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"შეეხეთ დესკტოპის სრული სარეზერვო ასლების პაროლის შესაცვლელად ან წასაშლელად"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ავტომატური"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView რეალიზაცია"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView რეალიზაციის დაყენება"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ფაილების დაშიფვრაზე გარდაქმნა"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"გარდაქმნა…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"უკვე დაშიფრულია ფაილების დონეზე"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ფერის კორექცია"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ეს ფუნქცია საცდელია და შეიძლება გავლენა იქონიოს შესრულებაზე."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"დაახლ. <xliff:g id="LEVEL">%1$s</xliff:g> დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> სრულ დატენვამდე"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ელკვებით სრულ დატენვამდე"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB-თი სრულ დატენვამდე"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> უსადენოდან სრულ დატენვამდე"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"უცნობი"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"იტენება"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"დატენვა ელკვებაზე"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"დატენვა USB-ზე"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"დატენვა უსადენოდ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"არ იტენება"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"არ იტენება"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index 1b8e176..b629522 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест мәндеріне қарамастан кез келген қолданбаны сыртқы жадқа жазуға жарамды етеді"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Әрекеттерді өлшемін өзгертуге болатын етуге мәжбүрлеу"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Манифест мәндеріне қарамастан барлық әрекеттерді бірнеше терезе үшін өлшемін өзгертуге болатын етеді."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Еркін пішіндегі терезелерді қосу"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Эксперименттік еркін пішіндегі терезелерді қолдауды қосады."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Компьютер үстелінің сақтық көшірмесі"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Жұмыс үстелінің сақтық көшірмелері қазір қорғалмаған"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Жұмыс үстелінің толық сақтық көшірмесінің кілтсөзін өзгерту немесе жою үшін түртіңіз"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Aвтоматты"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ендіру"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ендіруін орнату"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Файлды шифрлауға түрлендіру"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Түрлендіру..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Файл шифрланып қойылған"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Түсті түзету"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бұл мүмкіндік эксперименттік болып табылады және өнімділікке әсер етуі мүмкін."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - айнымалы токпен толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB арқылы толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - сымсыз толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Белгісіз"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядталуда"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Айнымалы токпен зар."</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB арқылы зарядтау"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Сымсыз зарядтау"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Зарядталу орындалып жатқан жоқ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Зарядталып тұрған жоқ"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Толық"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 0186a68..1155c07 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ធ្វើឲ្យកម្មវិធីទាំងឡាយមានសិទ្ធិសរសេរទៅកាន់ឧបករណ៍ផ្ទុកខាងក្រៅ ដោយមិនគិតពីតម្លៃជាក់លាក់"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"បង្ខំឲ្យសកម្មភាពអាចប្តូរទំហំបាន"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"កំណត់ឲ្យសកម្មភាពទាំងអស់អាចប្តូរទំហំបានសម្រាប់ពហុផ្ទាំងវិនដូ ដោយមិនគិតពីតម្លៃមេនីហ្វេសឡើយ។"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"បើកដំណើរការផ្ទាំងវិនដូទម្រង់សេរី"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"បើកដំណើរការគាំទ្រផ្ទាំងវិនដូទម្រង់សេរីសាកល្បង"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ពាក្យ​សម្ងាត់​បម្រុង​ទុក​លើ​ផ្ទៃតុ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ការ​បម្រុង​ទុក​ពេញលេញ​លើ​ផ្ទៃតុ​បច្ចុប្បន្ន​មិន​ត្រូវ​បាន​ការពារ​ទេ។"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ប៉ះ ដើម្បី​ប្ដូរ ឬ​លុប​ពាក្យ​សម្ងាត់​សម្រាប់​ការ​បម្រុងទុក​ពេញលេញ​លើ​ផ្ទៃតុ"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ស្វ័យប្រវត្តិ"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"ការប្រតិបត្តិ WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"កំណត់ការប្រតិបត្តិ WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"បម្លែងទៅជាការអ៊ីនគ្រីបឯកសារ"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"បម្លែង…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"បានអ៊ីនគ្រីបឯកសាររួចហើយ"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ការ​កែ​ពណ៌"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"លក្ខណៈ​នេះ​គឺ​ជា​ការ​ពិសោធន៍ ហើយ​អាច​ប៉ះពាល់​ការ​អនុវត្ត។"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"បដិសេធ​ដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅ​សល់​ប្រហែល <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់​ពេញ"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់ពេញ​រចន្ត​ឆ្លាស់"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់​ពេញ​តាមយូអេសប៊ី"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់​ពេញ​ពី​ឥតខ្សែ"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"មិន​ស្គាល់"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"កំពុងបញ្ចូល​ថ្ម"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"បញ្ចូលថ្មតាម AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"បញ្ចូលថ្មតាមយូអេសប៊ី"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"បញ្ចូលថ្មដោយ​​ឥតខ្សែ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"មិនកំពុង​បញ្ចូល​ថ្ម"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"មិន​បញ្ចូលថ្ម"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 56f4006..948c665315 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ಮ್ಯಾನಿಫೆಸ್ಟ್ ಮೌಲ್ಯಗಳನ್ನು ಪರಿಗಣಿಸದೇ, ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆಗೆ ಬರೆಯಲು ಅರ್ಹಗೊಳಿಸುತ್ತದೆ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ಚಟುವಟಿಕೆಗಳನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸುವಂತೆ ಒತ್ತಾಯ ಮಾಡಿ"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ಮ್ಯಾನಿಫೆಸ್ಟ್ ಮೌಲ್ಯಗಳನ್ನು ಪರಿಗಣಿಸದೇ, ಬಹು-ವಿಂಡೊಗೆ ಎಲ್ಲಾ ಚಟುವಟಿಕೆಗಳನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸುವಂತೆ ಮಾಡುತ್ತದೆ."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ಮುಕ್ತಸ್ವರೂಪದ ವಿಂಡೊಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ಪ್ರಾಯೋಗಿಕ ಮುಕ್ತಸ್ವರೂಪದ ವಿಂಡೊಗಳಿಗೆ ಬೆಂಬಲವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ಡೆಸ್ಕ್‌ಟಾಪ್ ಬ್ಯಾಕಪ್ ಪಾಸ್‌ವರ್ಡ್"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ಡೆಸ್ಕ್‌ಟಾಪ್‌‌ನ ಪೂರ್ಣ ಬ್ಯಾಕಪ್‌‌ಗಳನ್ನು ಪ್ರಸ್ತುತ ರಕ್ಷಿಸಲಾಗಿಲ್ಲ"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ಡೆಸ್ಕ್‌ಟಾಪ್‌ನ ಪೂರ್ಣ ಬ್ಯಾಕಪ್‌ಗಳಿಗೆ ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ಬದಲಾಯಿಸಲು ಅಥವಾ ತೆಗೆದುಹಾಕಲು ಸ್ಪರ್ಶಿಸಿ"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ಸ್ವಯಂಚಾಲಿತ"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿಸಿ"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"ಆಯ್ಕೆಮಾಡಲಾದ WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಮತ್ತು ಬಳಸಲು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕಾಗಿದೆ, ಇದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ನೀವು ಬಯಸುತ್ತೀರಾ?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ಫೈಲ್ ಎನ್‌ಕ್ರಿಪ್ಶನ್‌ಗೆ ಪರಿವರ್ತಿಸು"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ಪರಿವರ್ತಿಸು…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ಫೈಲ್ ಈಗಾಗಲೇ ಎನ್‌ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ಇದು ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯವಾಗಿದೆ. ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"ಸುಮಾರು <xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ಉಳಿದಿದೆ"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ನಲ್ಲಿ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ಮೂಲಕ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ವೈರ್‌‌ಲೆಸ್‌ನಿಂದ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"ಅಜ್ಞಾತ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ನಲ್ಲಿ ಚಾರ್ಜ್‌"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ಮೂಲಕ ಚಾರ್ಜ್‌"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ನಿಸ್ತಂತುವಾಗಿ ಚಾರ್ಜ್‌"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ಚಾರ್ಜ್‌ ಆಗುತ್ತಿಲ್ಲ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 6ad3efd..7f5cee2 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"매니페스트 값에 관계없이 앱을 외부 저장소에 작성"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"활동의 크기가 조정 가능하도록 설정"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"모든 활동을 매니페스트 값에 관계없이 멀티 윈도우용으로 크기 조정 가능하도록 설정"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"자유 형식 창 사용"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"자유 형식 창(베타) 지원 사용"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"데스크톱 백업 비밀번호"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"데스크톱 전체 백업에 비밀번호가 설정되어 있지 않음"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"데스크톱 전체 백업에 대한 비밀번호를 변경하거나 삭제하려면 터치하세요."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"자동"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 구현"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView 구현 설정"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"파일 암호화로 변환"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"변환..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"파일이 이미 암호화됨"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"색보정"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"실험실 기능이며 성능에 영향을 줄 수 있습니다."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 대략 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(AC 전원)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(USB)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(무선)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"알 수 없음"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"충전 중"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"충전 중(AC 전원)"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"충전 중(USB)"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"충전 중(무선)"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"충전 안함"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"충전 안함"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index bf37ac4..c00e827 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест маанилерине карабастан бардык колдонмолорду тышкы сактагычка сактоого уруксат берет"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Аракеттердин өлчөмүн өзгөртүүнү мажбурлоо"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Манифест маанилерине карабастан бардык аракеттерди мульти-терезеге өлчөмү өзгөртүлгүдөй кылат."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Эркин формадагы терезелерди түзүүнү иштетүү"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Эркин формадагы терезелерди түзүү боюнча сынамык функцияны иштетүү"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Компүтердеги бэкаптын сырсөзү"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Компүтердеги толук бэкап учурда корголгон эмес"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Тийип, компүтердеги толук бэкаптын сырсөзүн өзгөртүңүз же жок кылыңыз"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Автоматтык"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView аткарылышы"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView аткарылышын коюу"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Файл шифрлөөсүнө айландыруу"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Айландыруу…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Файл мурунтан эле шифрленген"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Түсүн тууралоо"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, иштин майнаптуулугуна таасир этиши мүмкүн."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - болжол менен <xliff:g id="TIME">%2$s</xliff:g> саат калды"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> толгончо"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC аркылуу толгончо"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB аркылуу толгончо"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> зымсыз кубаттоо аркылуу толгончо"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Белгисиз"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Кубатталууда"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ӨА кубатталууда"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB\'ден кубатталууда"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Зымсыз кубатталууда"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Кубат алган жок"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Кубатталган жок"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index 9e6a4b7..6dd3097 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ເຮັດ​ໃຫ້ທຸກແອັບ​ມີ​ສິດ​ໄດ້ຮັບການຂຽນ​ໃສ່​ບ່ອນ​ຈັດ​ເກັບ​ພາຍນອກ, ໂດຍ​ບໍ່​ຄຳ​ນຶງ​ເຖິງ​ຄ່າ​ທີ່​ຈະ​ແຈ້ງ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ບັງ​ຄັງ​ໃຫ້​ກິດ​ຈະ​ກຳ​ປ່ຽນ​ຂະ​ໜາດ​ໄດ້"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ເຮັດ​ໃຫ້​ທຸກ​ກິດ​ຈະ​ກຳ​ປ່ຽນ​ຂະ​ໜາດ​ໄດ້​ສຳ​ລັບ​ຫຼາຍ​ໜ້າ​ຕ່າງ, ໂດຍ​ບໍ່​ຄຳ​ນຶງ​ເຖິງ​ຄ່າ​ທີ່​ຈະ​ແຈ້ງ."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ເປີດໃຊ້ໜ້າຕ່າງຮູບແບບອິດສະຫຼະ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ເປີດໃຊ້ການຮອງຮັບໜ້າຕ່າງຮູບແບບອິດສະຫຼະທີ່ທົດລອງໃຊ້."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ລະຫັດຜ່ານການສຳຮອງຂໍ້ມູນເດັກສະທັອບ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ການ​ສຳຮອງ​ຂໍ້ມູນ​ເຕັມຮູບແບບ​ໃນ​ເດັກສະທັອບ​ຍັງ​ບໍ່​ໄດ້​ຮັບ​ການ​ປ້ອງກັນ​ໃນ​ເວລາ​ນີ້"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ແຕະເພື່ອປ່ຽນ ຫຼືລຶບລະຫັດຂອງການສຳຮອງຂໍ້ມູນເຕັມຮູບແບບໃນເດັກສະທັອບ"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ອັດຕະໂນມັດ"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ຕັ້ງການຈັດຕັ້ງປະຕິບັດ WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ປ່ຽນ​ເປັນ​ການ​ເຂົ້າ​ລະ​ຫັດ​ໄຟ​ລ໌"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ປ່ຽນ..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ໄຟ​ລ໌​ເຂົ້າ​ລະ​ຫັດ​ຮຽບ​ຮ້ອຍ​ແລ້ວ"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ການ​ປັບ​ແຕ່ງ​ສີ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"​ຄຸນ​ສົມ​ບັດ​ນີ້​ກຳ​ລັງ​ຢູ່​ໃນ​ການ​ທົດ​ລອງ​ແລະ​ອາດ​ມີ​ຜົນ​ຕໍ່​ປະ​ສິດ​ທິ​ພາບ."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ​ເຫຼືອປະ​ມານ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງ​ຈະ​ເຕັມ"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ​ຈຶ່ງ​ຈະ​ເຕັມ​ໂດຍສາກ​ດ້ວຍ​ໄຟ AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງ​ຈະ​ເຕັມ​ໂດຍສາກ​ດ້ວຍ USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ​ຈຶ່ງ​ຈະ​ເຕັມ​ໂດຍ​ສາກ​ແບບ​ໄຮ້​ສາຍ"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"ບໍ່ຮູ້ຈັກ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ກຳລັງສາກໄຟ"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ກຳ​​ລັງ​ສາກ​ຜ່ານ​ໝໍ້​ໄຟ"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"ກຳ​ລັງ​ສາກ​ຜ່ານ USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ກຳ​ລັງ​ສາກ​ໄຮ້​ສາຍ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ບໍ່ໄດ້ສາກໄຟ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ບໍ່ໄດ້ສາກໄຟ"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 2019646..6010c7a 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Vis. pr. gal. įr. į vid. saug. nepais. apr. vert."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Priv. nust., kad veiksm. b. g. atl. kelių d. lang."</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Nustatoma, kad visus veiksmus būtų galima atlikti kelių dydžių languose, nepaisant aprašo verčių."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Įgalinti laisvos formos langus"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Įgalinamas eksperimentinių laisvos formos langų palaikymas."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Viet. atsrg. kop. slapt."</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Šiuo metu visos vietinės atsarginės kopijos neapsaugotos"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Jei norite pakeisti ar pašalinti visų vietinių atsarginių kopijų slaptažodį, palieskite"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatinė"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"„WebView“ diegimas"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"„WebView“ diegimo nustatymas"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertuoti į failų šifruotę"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertuoti…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Jau konvertuota į failų šifruotę"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Spalvų taisymas"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ši funkcija yra eksperimentinė ir ji gali turėti įtakos našumui."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko maždaug <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naud. kint. sr."</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naudojant USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo belaid. ryš."</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Nežinomas"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Kraunasi..."</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Įkr. naud. kint. sr."</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Įkraunama naud. USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Įkraunama be laidų"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nekraunama"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nekraunama"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2f9a83b..d50e1ea 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Ļauj jebkuru lietotni ierakstīt ārējā krātuvē neatkarīgi no manifesta vērtības."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Pielāgot darbības"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Pielāgo visas darbības vairāku logu režīmam neatkarīgi no vērtībām manifestā."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Iespējot brīvās formas logus"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Iespējo eksperimentālo brīvās formas logu atbalstu."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Datora dublējuma parole"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Darbvirsmas pilnie dublējumi pašlaik nav aizsargāti."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Pieskarieties, lai mainītu vai noņemtu paroli pilniem darbvirsmas dublējumiem."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automātiski"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ieviešana"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Iestatīt WebView ieviešanu"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Pārvērst par failu šifrējumu"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pārvērst…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Jau šifrēts failu līmenī"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Krāsu korekcija"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Šī funkcija ir eksperimentāla un var ietekmēt veiktspēju."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> — aptuvenais atlikušais laiks: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai maiņstrāvas uzlādei"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai USB uzlādei"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai bezvadu uzlādei"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Maiņstrāvas uzlāde"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB uzlāde"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezvadu uzlāde"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenotiek uzlāde"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenotiek uzlāde"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index 51c2568..03feddd 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Запишува апл. во надв.меморија, незав. од манифест"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Принуди ги активностите да ја менуваат големината"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Ги прави сите активности да бидат со променлива големина за мултипрозорец, без разлика на вредностите на манифестот."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Овозможи прозорци со слободна форма"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Овозможува поддршка за експериментални прозорци со слободна форма."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Резервна лозинка за работна површина"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Целосни резервни копии на работната површина кои во моментов не се заштитени"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Допрете за да се промени или отстрани лозинката за целосна резервна копија на работната површина"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Автоматски"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Воведување WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Поставете воведување WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Конвертирајте до шифрирање датотеки"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Конвертирај..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Датотеката е веќе шифрирана"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекција на боја"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Функцијата е експериментална и може да влијае на изведбата."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостанува приближно <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна на AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна преку USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна, безжично"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Се полни"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Полнење на струја"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Полнење преку УСБ"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично полнење"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Не се полни"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се полни"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Целосна"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index f00103f..e47afc4 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, ബാഹ്യ സ്റ്റോറേജിലേക്ക് എഴുതപ്പെടുന്നതിന് ഏതൊരു ആപ്പിനെയും യോഗ്യമാക്കുന്നു"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"വലിപ്പം മാറ്റാൻ പ്രവർത്തനങ്ങളെ നിർബന്ധിക്കുക"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"മാനിഫെസ്റ്റ് മൂല്യങ്ങൾ പരിഗണിക്കാതെ, എല്ലാ പ്രവർത്തനങ്ങളെയും മൾട്ടി-വിൻഡോയ്ക്കായി വലിപ്പം മാറ്റുന്നു."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"ഫ്രീഫോം വിൻഡോകൾ പ്രവർത്തനക്ഷമമാക്കുക"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"പരീക്ഷണാത്മക ഫ്രീഫോം വിൻഡോകൾക്കുള്ള പിന്തുണ പ്രവർത്തനക്ഷമമാക്കുന്നു."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ഡെ‌സ്‌ക്ടോപ്പ് ബാക്കപ്പ് പാസ്‌വേഡ്"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ഡെസ്‌ക്‌ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾ നിലവിൽ പരിരക്ഷിച്ചിട്ടില്ല"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ഡെസ്‌ക്‌ടോപ്പ് പൂർണ്ണ ബാക്കപ്പുകൾക്കായി പാസ്‌വേഡുകൾ മാറ്റാനോ നീക്കംചെയ്യാനോ സ്‌പർശിക്കുക"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ഓട്ടോമാറ്റിക്"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView നടപ്പാക്കൽ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView നടപ്പാക്കൽ സജ്ജമാക്കുക"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ഫയൽ എൻക്രിപ്ഷനിലേക്ക് പരിവർത്തിപ്പിക്കുക"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"പരിവർത്തിപ്പിക്കുക…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ഇതിനകം തന്നെ ഫയൽ എൻക്രിപ്റ്റ് ചെയ്തു"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"വർണ്ണം ക്രമീകരിക്കൽ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ഈ ഫീച്ചർ പരീക്ഷണാത്മകമായതിനാൽ പ്രകടനത്തെ ബാധിച്ചേക്കാം."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഏകദേശം <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - AC-യിൽ പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - വയർലെസ് വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"അജ്ഞാതം"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ചാർജ്ജുചെയ്യുന്നു"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-യിൽ ചാർജ്ജുചെയ്യുന്നു"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-യിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"വയർലെസ്സ് കണക്ഷനിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index cc0e7cb..01925f7 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Манифест утгыг нь үл хамааран дурын апп-ыг гадаад санах ойд бичих боломжтой болгодог"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Үйл ажиллагааны хэмжээг өөрчилж болохуйц болгох"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Тодорхойлогч файлын утгыг үл хамааран, бүх үйл ажиллагааг олон цонхонд хэмжээг нь өөрчилж болохуйц болгох."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Чөлөөт хэлбэрийн цонхыг идэвхжүүлэх"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Туршилтын чөлөөт хэлбэрийн цонхны дэмжлэгийг идэвхжүүлдэг."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Десктоп нөөшлөлтийн нууц үг"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Десктоп бүрэн нөөцлөлт одоогоор хамгаалалтгүй байна"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Десктоп дээрх бүрэн нөөшлөлтийн нууц үгийг өөрчлөх буюу арилгахын тулд хүрнэ үү"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Автоматаар"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView хэрэгжилт"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView хэрэгжилтийг тохируулах"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Файлын шифрлэлт болгон хөрвүүлэх"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Хөрвүүлэх..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Аль хэдийнэ файл шифрлэгдсэн"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Өнгө тохируулах"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Энэ функц туршилтынх бөгөөд ажиллагаанд нөлөөлж болзошгүй."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ойролцоогоор <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"АС-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"USB-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"утасгүй цэнэглэгчээр дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Тодорхойгүй"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Цэнэглэж байна"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-р цэнэглэж байна"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-р цэнэглэж байна"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Кабльгүйгээр цэнэглэж байна"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Цэнэглэхгүй байна"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Цэнэглэхгүй байна"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index b939ca0..804fab0 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"मॅनिफेस्ट मूल्यांकडे दुर्लक्ष करून, कोणत्याही अॅपला बाह्य संचयनावर लेखन केले जाण्‍यासाठी पात्र बनविते"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"क्र‍ियाकलापाचा आकार बदलण्यायोग्य होण्याची सक्ती करा"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"मॅनिफेस्ट मूल्यांकडे दुर्लक्ष करून, एकाधिक-विंडोसाठी सर्व क्रियाकलापांचा आकार बदलण्यायोग्य करा"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"freeform विंडो सक्षम करा"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"प्रायोगिक मुक्तस्वरूपाच्या विंडोसाठी समर्थन सक्षम करते."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटॉप बॅकअप संकेतशब्द"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटॉप पूर्ण बॅक अप सध्या संरक्षित नाहीत"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटॉपच्या पूर्ण बॅकअपसाठी असलेला संकेतशब्द बदलण्यासाठी किंवा काढून टाकण्यासाठी स्पर्श करा"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"स्वयंचलित"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"वेबदृश्य अंमलबजावणी"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"वेबदृश्य अंमलबजावणी सेट करा"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"निवडलेली WebView अंमलबजावणी अक्षम आहे आणि वापरण्यास सक्षम असणे आवश्यक आहे, आपण ती सक्षम करू इच्छिता?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"फाईल कूटबद्धीकरणावर रूपांतरित करा"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करा..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फाईल आधीपासून कूटबद्ध केली"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"रंग सुधारणा"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"हे वैशिष्‍ट्य प्रायोगिक आहे आणि कदाचित कार्यप्रदर्शन प्रभावित करू शकते."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - अंदाजे. <xliff:g id="TIME">%2$s</xliff:g> शिल्लक"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण होण्यात"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC वरून पूर्ण होण्यात"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB वरून पूर्ण होण्यात"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसवरून पूर्ण होण्यात"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज होत आहे"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC वर चार्ज करीत आहे"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB वरून चार्ज करीत आहे"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस वरून चार्ज करीत आहे"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज होत नाही"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज होत नाही"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 2e67752..ed4d071 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Menjadikan sebarang apl layak ditulis ke storan luaran, walau apa juga nilai manifesnya"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Paksa aktiviti supaya boleh diubah saiz"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Menjadikan semua aktiviti boleh diubah saiz untuk berbilang tetingkap, tanpa mengambil kira nilai manifes."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Dayakan tetingkap bentuk bebas"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Mendayakan sokongan untuk tetingkap bentuk bebas percubaan."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Kata laluan sandaran komputer meja"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Sandaran penuh komputer meja tidak dilindungi pada masa ini"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Sentuh untuk menukar atau mengalih keluar kata laluan untuk sandaran penuh komputer meja"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatik"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Pelaksanaan WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Tetapkan pelaksanaan WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Tukar kepada penyulitan fail"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Tukar..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sudah disulitkan fail"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Pembetulan warna"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ciri ini adalah percubaan dan boleh menjejaskan prestasi."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira. <xliff:g id="TIME">%2$s</xliff:g> yang tinggal"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh di AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh melalui USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh dari wayarles"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengecas"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengecas pada AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Mengecas melalui USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Mengecas tanpa wayar"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengecas"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengecas"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 002aed1..ab7a8d0 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ပြနေတဲ့ တန်ဖိုး ဘယ်လိုပဲရှိနေနေ၊ ဘယ် appကို မဆို အပြင် သိုလှောင်ခန်းသို့ ရေးသားခွင့် ပေးတယ်"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"လုပ်ဆောင်ချက်များ ဆိုက်ညှိရနိုင်ရန် လုပ်ခိုင်းပါ"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"မန်နီးဖက်စ် တန်ဖိုးမရွေး၊ လုပ်ဆောင်ချက် အားလုံး ဆိုက်ညှိရနိုင်အောင် လုပ်ပေးပါ။"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"အခမဲ့ပုံစံ ဝင်းဒိုးကို ဖွင့်ပါ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"စမ်းသပ်မှု အခမဲ့ပုံစံ ဝင်းဒိုးများအတွက် ပံ့ပိုးမှုကို ဖွင့်ပါ။"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Desktop အရန်စကားဝှက်"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"အလုပ်ခုံတွင် အရန်သိမ်းဆည်းခြင်းများကို လောလောဆယ် မကာကွယ်နိုင်ပါ။"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"အလုပ်ခုံ တွင် အရန်သိမ်းဆည်းခြင်းအပြည့်လုပ်ရန် အတွက် စကားဝှက်ဖယ်ရန် သို့ ပြောင်းရန် တို့ထိပါ။"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"အလိုအလျောက်"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView အကောင်အထည်ဖော်မှု"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView အကောင်အထည်ဖော်မှု သတ်မှတ်ပါ"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ဖိုင်လုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းပါ"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ပြောင်းရန်…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ဖိုင်ကို လုံခြုံအောင်ပြုလုပ်ပြီးပါပြီ"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"အရောင်ပြင်ဆင်မှု"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ဒီအင်္ဂါရပ်မှာ စမ်းသပ်မှု ဖြစ်၍ လုပ်ကိုင်မှုကို အကျိုးသက်ရောက်နိုင်သည်။"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ခန့်မှန်းခြေ။ <xliff:g id="TIME">%2$s</xliff:g> ကျန်ရှိနေ"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> အပြည့်အထိ"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> လျှပ်စစ်ဖြင့် အပြည့်အထိ"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ဖြင့် အပြည့်အထိ"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ကြိုးမဲ့ဖြင့် အပြည့်အထိ"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"အကြောင်းအရာ မသိရှိ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"လျှပ်စစ်ဖြင့် အားသွင်းနေ"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBဖြင့် အားသွင်းနေ"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ကြိုးမဲ့ အားသွင်းနေ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"အားသွင်းမနေပါ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"အားသွင်းမနေပါ"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index a496a21..e39cf30 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Gjør at apper kan skrives til ekstern lagring, uavhengig av manifestverdier"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Tving aktiviteter til å kunne endre størrelse"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Dette gjør at alle aktivitene kan endre størrelse for flervindusmodus, uavhengig av manifestverdier."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Slå på vinduer i fritt format"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Slår på støtte for vinduer i eksperimentelt fritt format."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Passord for sikkerhetskopiering på datamaskin"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Fullstendig sikkerhetskopiering på datamaskin beskyttes ikke for øyeblikket."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Trykk for å endre eller fjerne passordet for fullstendige sikkerhetskopier på datamaskinen"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatisk"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Angi WebView-implementering"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Den valgte implementeringen av nettvisningen er slått av – den må slås på for å brukes. Vil du slå den på?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertér til kryptert fil"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér …"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede kryptert og lagret som fil"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Fargekorrigering"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funksjonen er eksperimentell og kan påvirke ytelsen."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> igjen"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> –  fulladet om <xliff:g id="TIME">%2$s</xliff:g> med vekselstrøm"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via trådløs lading"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukjent"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Lader"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Lader via strømuttak"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Lader via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Lader trådløst"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Lader ikke"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Lader ikke"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 5c85c25..d9cfbcc 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"म्यानिफेेस्टको उपेक्षा गरी, कुनै पनि अनुप्रयोगलाई बाह्य भण्डारणमा लेख्न योग्य बनाउँछ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"गतिविधिहरू रिसाइज गर्नको लागि बाध्य गर्नुहोस्"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"म्यानिफेेस्ट मानहरूको ख्याल नगरी, बहु-विन्डोको लागि सबै रिसाइज गर्न सकिने गतिविधिहरू बनाउँछ।"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"फ्रिफर्म विन्डोहरू सक्रिय गर्नुहोस्"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"प्रयोगात्मक फ्रिफर्म विन्डोहरूका लागि समर्थनलाई सक्रिय गर्छ।"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटप ब्याकअप पासवर्ड"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटप पूर्ण जगेडाहरू हाललाई सुरक्षित छैनन्"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"डेस्कटप पूर्ण ब्याकअपको लागि पासवर्ड बदल्न वा हटाउन छुनुहोस्"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"स्वचालित"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट गर्नुहोस्"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"फाइल इन्क्रिप्सनमा रूपान्तरण गर्नुहोस्"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रुपान्तरण गर्नुहोस्…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"पहिल्यै फाइल इन्क्रिप्ट गरिएको छ"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"रङ्ग सुधार"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यो सुविधा प्रयोगात्मक छ र प्रदर्शनमा असर गर्न सक्छ।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग। <xliff:g id="TIME">%2$s</xliff:g> बायाँ"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण नभए सम्म"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC मा पूर्ण नभए सम्म"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB मा पूर्ण नभए सम्म"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसबाट पूर्ण नभए सम्म"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हुँदै"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC मा चार्ज गर्दै"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB मा चार्ज गर्दै"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"बिना तार चार्ज गर्दै"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज भइरहेको छैन"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज हुँदै छैन"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 1afa444..4873ac3 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Hierdoor komt een app in aanmerking om te worden geschreven naar externe opslag, ongeacht de manifestwaarden"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Formaat activiteiten geforceerd aanpasbaar maken"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Hiermee wordt het formaat van alle activiteiten aanpasbaar gemaakt, ongeacht de manifestwaarden."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Vensters met vrije vorm inschakelen"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Schakelt ondersteuning in voor vensters met experimentele vrije vorm."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Wachtwoord desktopback-up"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Volledige back-ups naar desktops zijn momenteel niet beveiligd"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Raak dit aan om het wachtwoord voor volledige back-ups naar desktops te wijzigen of te verwijderen"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatisch"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementatie"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-implementatie instellen"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"De gekozen WebView-implementatie is uitgeschakeld en moet worden ingeschakeld voor gebruik. Wil je deze inschakelen?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converteren naar versleuteling op basis van bestanden"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converteren…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Al versleuteld op basis van bestanden"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kleurcorrectie"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Deze functie is experimenteel en kan invloed hebben op de prestaties."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ca. <xliff:g id="TIME">%2$s</xliff:g> resterend"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via wisselstroom"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via draadloos"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Opladen"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladen via netvoeding"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladen via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Draadloos opladen"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Wordt niet opgeladen"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wordt niet opgeladen"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index ad3d3ed..61f187d 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ਇੱਕ ਐਪ ਨੂੰ ਬਾਹਰਲੀ ਸਟੋਰੇਜ ਤੇ ਲਿਖਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ, ਮੈਨੀਫੈਸਟ ਵੈਲਯੂਜ ਤੇ ਵਿਚਾਰ ਕੀਤੇ ਬਿਨਾਂ"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ਮੁੜ-ਆਕਾਰ ਬਦਲਣ ਲਈ ਸਰਗਰਮੀਆਂ \'ਤੇ ਜ਼ੋਰ ਦਿਓ"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ਮਲਟੀ-ਵਿੰਡੋ ਲਈ ਸਾਰੀਆਂ ਸਰਗਰਮੀਆਂ ਨੂੰ ਮੁੜ-ਆਕਾਰ ਵਿੱਚ ਲਿਆਉਂਦੀ ਹੈ, ਚਾਹੇ ਮੈਨੀਫੈਸਟ ਵੈਲਯੂਜ਼ ਕੁਝ ਵੀ ਹੋਣ।"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"freeform windows ਨੂੰ ਯੋਗ ਬਣਾਓ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ਪ੍ਰਯੋਗਾਤਮਕ freeform windows ਲਈ ਸਮਰਥਨ ਨੂੰ ਯੋਗ ਬਣਾਉਂਦੀ ਹੈ।"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ਡੈਸਕਟੌਪ ਬੈਕਅਪ ਪਾਸਵਰਡ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ਡੈਸਕਟੌਪ ਪੂਰੇ ਬੈਕਅਪਸ ਇਸ ਵੇਲੇ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹਨ"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ਡੈਸਕਟੌਪ ਪੂਰੇ ਬੈਕਅਪਸ ਲਈ ਪਾਸਵਰਡ ਬਦਲਣ ਜਾਂ ਹਟਾਉਣ ਲਈ ਛੋਹਵੋ"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ਆਟੋਮੈਟਿਕ"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ਅਮਲ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ਅਮਲ ਸੈੱਟ ਕਰੋ"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"ਚੁਣਿਆ ਗਿਆ WebView ਅਮਲ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ, ਅਤੇ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇਸ ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਜਾਣਾ ਜ਼ਰੂਰੀ ਹੈ, ਕੀ ਤੁਸੀਂ ਇਸ ਨੂੰ ਯੋਗ ਬਣਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ਫ਼ਾਈਲ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰੋ"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ਤਬਦੀਲ ਕਰੋ ..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਤੋਂ ਇਨਕ੍ਰਿਪਟਡ ਹੈ"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ਰੰਗ ਸੰਸ਼ੋਧਨ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਾਤਮਿਕ ਹੈ ਅਤੇ ਪ੍ਰਦਰਸ਼ਨ ਤੇ ਅਸਰ ਪਾ ਸਕਦੀ ਹੈ।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਲਗਭਗ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਵਾਇਰਲੈਸ ਤੋਂ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"ਅਗਿਆਤ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ਚਾਰਜਿੰਗ"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ਤੇ ਚਾਰਜਿੰਗ"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ਤੇ ਚਾਰਜਿੰਗ"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ਵਾਇਰਲੈਸ ਤੌਰ ਤੇ ਚਾਰਜਿੰਗ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index d58070e..0189fab 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Pozwala na zapis aplikacji w pamięci zewn. niezależnie od wartości w pliku manifestu"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Wymuś zmianę rozmiaru okien aktywności"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Umożliwia zmianę rozmiaru wszystkich okien aktywności w trybie wielu okien niezależnie od ustawień w pliku manifestu."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Włącz dowolny rozmiar okien"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Włącza obsługę eksperymentalnej funkcji dowolnego rozmiaru okien."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Hasło kopii zapasowej"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Pełne kopie zapasowe na komputerze nie są obecnie chronione"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Wybierz, aby zmienić lub usunąć hasło pełnych kopii zapasowych na komputerze stacjonarnym."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatycznie"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementacja WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ustaw implementację WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Przekształć na szyfrowanie plików"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Przekształć…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Pliki są już zaszyfrowane"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcja kolorów"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To jest funkcja eksperymentalna i może wpływać na działanie urządzenia."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostało ok. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania z gniazdka"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania przez USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania bezprzewodowo"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Nieznane"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Ładowanie"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Ładowanie zasilaczem"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Ładowanie przez USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ład. bezprzewodowe"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nie podłączony"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nie podłączony"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 3b28043..ee701a6 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualifica apps p/ gravação em armazenamento externo, independentemente de valores de manifestos"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ativar janelas de forma livre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ativa a compatibilidade com janelas de forma livre experimentais."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Senha do backup local"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Os backups completos do computador não estão protegidos no momento"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a senha de backups completos do desktop"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação do WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar implementação do WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para criptografia de arquivos"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Já criptografado com base em arquivos"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correção de cor"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index c5f94cb..2f25ae2 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualquer aplic. pode ser gravada no arm. ext., independ. dos valores do manif."</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar as atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ativar janelas de forma livre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ativa a compatibilidade com janelas de forma livre experimentais."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Palavra-passe cópia do comp."</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"As cópias de segurança completas no ambiente de trabalho não estão atualmente protegidas"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a palavra-passe para cópias de segurança completas no ambiente de trabalho"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Definir implementação WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"A implementação WebView escolhida foi desativada e tem de ser ativada para poder ser utilizada. Pretende ativá-la?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para a encriptação de ficheiros"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Os ficheiros já estão encriptados"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correção da cor"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta funcionalidade é experimental e pode afetar o desempenho."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – resta(m) aprox. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até ficar compl. por rede s/ fios"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"A carregar"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"A carregar por CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"A carregar por USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"A carregar sem fios"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está a carregar"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está a carregar"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Completo"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 3b28043..ee701a6 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Qualifica apps p/ gravação em armazenamento externo, independentemente de valores de manifestos"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar atividades a serem redimensionáveis"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Torna todas as atividades redimensionáveis para várias janelas, independentemente dos valores do manifesto."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Ativar janelas de forma livre"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ativa a compatibilidade com janelas de forma livre experimentais."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Senha do backup local"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Os backups completos do computador não estão protegidos no momento"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Toque para alterar ou remover a senha de backups completos do desktop"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação do WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar implementação do WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para criptografia de arquivos"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Já criptografado com base em arquivos"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correção de cor"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index c76faf0..29d878f 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Face orice aplicație eligibilă să fie scrisă în stocarea externă, indiferent de valorile manifestului"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Forțați redimensionarea activităților"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Activați ferestrele cu formă liberă"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Activează compatibilitatea pentru ferestrele experimentale cu formă liberă."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Parolă copie rez. desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"În prezent, copiile de rezervă complete pe desktop nu sunt protejate"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Atingeţi pentru a modifica sau pentru a elimina parola pentru copiile de rezervă complete pe desktop"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automat"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementare WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Setați implementarea WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Faceți conversia la criptarea bazată pe sistemul de fișiere"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertiți…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Criptarea bazată pe sistemul de fișiere este finalizată"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corecția culorii"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Această funcție este experimentală și poate afecta performanțele."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – timp rămas: aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă la c.a."</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă prin USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă wireless"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Încarcă"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Se încarcă la C.A."</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Se încarcă prin USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Se încarcă fără fir"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nu se încarcă"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nu încarcă"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index f1cd182..ba21d3e 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Разрешает сохранение приложений на внешние накопители независимо от значения манифеста"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Изменение размера в многооконном режиме"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Позволяет менять размер в многооконном режиме (независимо от значений манифеста)"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Разрешить создание окон произвольной формы"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Включить экспериментальную функцию создания окон произвольной формы"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Пароль для резервного копирования"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Полные резервные копии в настоящее время не защищены"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Изменить или удалить пароль для резервного копирования"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Автоматическое переключение"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Сервис WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Настройки сервиса WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Переход к шифрованию файлов"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Перейти…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Шифрование файлов уже включено"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Коррекция цвета"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Это экспериментальная функция, она может снизить производительность устройства."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – осталось около <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (от сети)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (через USB)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (беспроводная)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Идет зарядка"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зарядка от сети"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зарядка через USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Беспроводная зарядка"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряжается"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряжается"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index eb13ce4..0f32a0c 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"මැනිෆෙස්ට් අගයන් නොසලකා, ඕනෑම යෙදුමක් අභ්‍යන්තර ගබඩාවට ලිවීමට සුදුසුකම් ලබා දෙයි"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"ක්‍රියාකාරකම් ප්‍රතිප්‍රමාණ කළ හැකි බවට බල කරන්න"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"මැනිෆෙස්ට් අගයන් නොසලකා, සියලු ක්‍රියාකාරකම් බහු-කවුළු සඳහා ප්‍රතිප්‍රමාණ කළ හැකි බවට පත් කරයි."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"අනියම් හැඩැති කවුළු සබල කරන්න"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"පරීක්ෂණාත්මක අනියම් හැඩැති කවුළු සඳහා සහාය සබල කරයි."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ඩෙස්ක්ටොප් උපස්ථ මුරපදය"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ දැනට ආරක්ෂා කර නොමැත"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ඩෙස්ක්ටොප් සම්පූර්ණ උපස්ථ සඳහා මුරපදය වෙනස් කිරීමට හෝ ඉවත් කිරීමට ස්පර්ශ කරන්න"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"ස්වයංක්‍රීය"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ක්‍රියාත්මක කිරීම"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ක්‍රියාත්මක කිරීම සකසන්න"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ගොනු සංකේතනයට පරිවර්තනය කරන්න"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"පරිවර්තනය කරන්න..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"දැනටමත් ගොනුව සංකේතනය කර ඇත"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"වර්ණ නිවැරදි කිරීම"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"මෙම විශේෂාංගය පරීක්ෂණාත්මක සහ ඇතැම් විට ක්‍රියාකාරිත්වයට බලපෑ හැක."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආසන්න <xliff:g id="TIME">%2$s</xliff:g> වම"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"AC හි <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"USB හරහ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"රේඩියෝව වෙතින් <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පූර්ණ වන තෙක්"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"නොදනී"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ආරෝපණය වෙමින්"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC හි ආරෝපණය වෙමින්"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB හරහා ආරෝපණය වෙමින්"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"රැහැන් රහිතව ආරෝපණය වෙමින්"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ආරෝපණය නොවේ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ආරෝපණය නොවෙමින්"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index b5b6d2a..8df02ea 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Umožňuje zapísať akúkoľvek aplikáciu do externého úložiska bez ohľadu na hodnoty v manifeste"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Vynútiť možnosť zmeny veľkosti aktivít"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Veľkosti všetkých aktivít bude možné zmeniť na niekoľko okien (bez ohľadu na hodnoty manifestu)."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Povoliť okná s voľným tvarom"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Povolenie podpory pre experimentálne okná s voľným tvarom."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Heslo pre zálohy v počítači"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Úplné zálohy na počítači nie sú momentálne chránené"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dotykom zmeníte alebo odstránite heslo pre úplné zálohy do počítača"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatický"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementácia komponenta WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavenie implementácie komponenta WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Zvolená implementácia technológie WebView je zakázaná. Ak ju chcete použiť, musíte ju najprv povoliť. Chcete ju povoliť?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertovať na šifrovanie súborov"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertovať…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Súbory sú už šifrované"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Úprava farieb"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkcia je experimentálna a môže mať vplyv na výkonnosť."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva približne <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia zo zásuvky"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia cez USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia bezdrôtovo"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjanie"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjanie zo zásuvky"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjanie cez USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrôtové nabíjanie"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíja sa"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíja sa"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index cb107d7..5d47af9 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsako aplikacijo zapisati v zunanjo shrambo"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Vsili povečanje velikosti za aktivnosti"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsem aktivnostim povečati velikost za način z več okni."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Omogočanje oken svobodne oblike"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Omogočanje podpore za poskusna okna svobodne oblike"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Geslo za varn. kop. rač."</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Popolne varnostne kopije namizja trenutno niso zaščitene"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dotaknite se, če želite spremeniti ali odstraniti geslo za popolno varnostno kopiranje namizja."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Samodejno"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Izvedba spletnega pogleda"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavitev izvedbe spletnega pogleda"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Preklop na šifriranje podatkov"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Preklop …"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Šifriranje podatkov je že uveljavljeno"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Popravljanje barv"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To je preskusna funkcija in lahko vpliva na učinkovitost delovanja."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – še približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek napajalnika"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek USB-ja"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek brezž. pol."</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznano"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Polnjenje"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Polnj. prek iz. toka"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Polnj. prek USB-ja"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Brezžično polnjenje"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Se ne polni"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Se ne polni"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index c176f18..8e8112f 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Bën që çdo aplikacion të jetë i përshtatshëm për t\'u shkruar në hapësirën ruajtëse të jashtme, pavarësisht nga vlerat e manifestit"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Detyro madhësinë e ndryshueshme për aktivitetet"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Bën që të gjitha aktivitetet të kenë madhësi të ndryshueshme për përdorimin me shumë dritare, pavarësisht vlerave të manifestit."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivizo dritaret me formë të lirë"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktivizon mbështetjen për dritaret eksperimentale me formë të lirë."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Fjalëkalimi rezervë i kompjuterit"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Rezervimet e plota në kompjuter nuk janë të mbrojtura aktualisht"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Prek për të ndryshuar ose hequr fjalëkalimin për rezervime të plota të desktopit"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatike"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Zbatimi i WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Cakto zbatimin e WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konverto në enkriptimin e skedarit"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konverto..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Enkriptimi i skedarit është kryer tashmë"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korrigjimi i ngjyrës"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ky funksion është eksperimental dhe mund të ndikojë në veprimtari."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - afërsisht <xliff:g id="TIME">%2$s</xliff:g> të mbetura"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të jetë e plotë"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet në AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet me USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet nga lidhja pa tel"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"I panjohur"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Po ngarkohet"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Po ngarkohet në AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Po ngarkohet me USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Po ngarkohet me valë"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Nuk po ngarkohet"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nuk po ngarkohet"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 6b66094..935f44d 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Омогућава уписивање свих апликација у спољну меморију, без обзира на вредности манифеста"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Принудно омогући промену величине активности"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Омогућава промену величине свих активности за режим са више прозора, без обзира на вредности манифеста."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Омогући прозоре произвољног формата"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Омогућава подршку за експерименталне прозоре произвољног формата."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Лозинка резервне копије за рачунар"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Резервне копије читавог система тренутно нису заштићене"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Додирните да бисте променили или уклонили лозинку за прављење резервних копија читавог система на рачунару"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Аутоматски"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Примена WebView-а"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Подесите примену WebView-а"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Конвертуј у шифровање датотека"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Конвертуј..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Већ се користи шифровање датотека"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекција боја"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ова функција је експериментална и може да утиче на перформансе."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостало око <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни пуњачем"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни преко USB-а"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни бежично"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Пуњење"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Пуњење преко пуњача"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Пуњење преко USB-а"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Бежично пуњење"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Не пуни се"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не пуни се"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Пуно"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 40e1593..1bdf6b2 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Appen kan skrivas till extern lagring, oavsett manifestvärden"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Framtvinga storleksanpassning för aktiviteter"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Detta gör det möjligt att ändra storleken på alla aktiviteter i flerfönsterläge, oavsett manifestvärden."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Aktivera frihandsfönster"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiverar stöd för experimentella frihandsfönster."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Lösenord för säkerhetskopia av datorn"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"De fullständiga säkerhetskopiorna av datorn är för närvarande inte skyddade"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Tryck om du vill ändra eller ta bort lösenordet för fullständig säkerhetskopiering av datorn"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Automatiskt"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ange WebView-implementering"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Den valda WebView-implementeringen har inaktiverats och måste aktiveras om du ska kunna använda den. Vill du aktivera den?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertera till filkryptering"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertera …"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Filkryptering används redan"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Färgkorrigering"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Den här funktionen är experimentell och kan påverka prestandan."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca <xliff:g id="TIME">%2$s</xliff:g> kvar"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via laddare"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via trådlös laddning"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Okänd"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Laddar"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laddas via adapter"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laddas via USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laddas trådlöst"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Laddar inte"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laddar inte"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index a014e1d..8a5ddd0 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Huweka programu kwenye hifadhi ya nje, bila kujali maelezo"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Lazimisha shughuli ziweze kubadilishwa ukubwa"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Fanya shughuli zote ziweze kubadilishwa ukubwa kwa ajili ya dirisha nyingi, bila kujali thamani za faili ya maelezo."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Washa madirisha yenye muundo huru"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Huwasha uwezo wa kutumia madirisha ya majaribio yenye muundo huru."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Nenosiri la hifadhi rudufu ya eneo kazi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Hifadhi rudufu kamili za eneo kazi hazijalindwa kwa sasa"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Gusa ili ubadilishe au uondoe nenosiri la hifadhi rudufu kamili za eneo kazi"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Otomatiki"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Weka utekelezaji wa WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Badilisha kuwa usimbaji fiche wa faili"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Badilisha..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Tayari faili imesimbwa kwa njia fiche"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Usahihishaji wa rangi"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Kipengele hiki ni cha majaribio na huenda kikaathiri utendaji."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia takriban <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>%% - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa isiyotumia waya"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Haijulikani"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Inachaji"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Inachaji kupitia AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Inachaji kupitia USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Inachaji bila kutumia waya"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Haichaji"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Haichaji"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index 4f6ea57..42a5534 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"மேனிஃபெஸ்ட் மதிப்புகளை பொருட்படுத்தாமல், எந்தப் பயன்பாட்டையும் வெளிப்புற சேமிப்பிடத்தில் எழுத அனுமதிக்கும்"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"செயல்பாடுகளை அளவுமாறக்கூடியதாக அமை"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"மேனிஃபெஸ்ட் மதிப்புகளைப் பொருட்படுத்தாமல், பல சாளரத்திற்கு எல்லா செயல்பாடுகளையும் அளவுமாறக்கூடியதாக அமைக்கும்."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"குறிப்பிட்ட வடிவமில்லாத சாளரங்களை இயக்கு"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"பரிசோதனைக்குரிய குறிப்பிட்ட வடிவமில்லாத சாளரங்களுக்கான ஆதரவை இயக்கும்."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"டெஸ்க்டாப் காப்புப்பிரதி கடவுச்சொல்"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"டெஸ்க்டாப்பின் முழு காப்புப்பிரதிகள் தற்போது பாதுகாக்கப்படவில்லை"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"டெஸ்க்டாப்பின் முழுமையான காப்புப்பிரதிகளுக்கான கடவுச்சொல்லை மாற்றுவதற்கு அல்லது அகற்றுவதற்குத் தொடவும்"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"தானியங்கு"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView செயல்படுத்தல்"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView செயல்படுத்தலை அமை"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"கோப்பு முறைமையாக்கத்திற்கு மாற்று"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"மாற்று…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ஏற்கனவே கோப்பு முறைமையாக்கப்பட்டது"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"வண்ணத்திருத்தம்"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"இது சோதனை முறையிலான அம்சம், இது செயல்திறனைப் பாதிக்கலாம்."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"தோராயம்: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> உள்ளது"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"முழு சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"முழு AC சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"முழு USB சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"முழு வயர்லெஸ் சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"அறியப்படாத"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"சார்ஜ் ஏற்றப்படுகிறது"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC மூலம் சார்ஜாகிறது"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB மூலம் சார்ஜாகிறது"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"வயர்லெஸில் சார்ஜாகிறது"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"சார்ஜ் செய்யப்படவில்லை"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"சார்ஜ் ஏறவில்லை"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"முழுமை"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index a6d2140..04f0ca0 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ఏ అనువర్తనాన్ని అయినా మానిఫెస్ట్ విలువలతో సంబంధం లేకుండా బాహ్య నిల్వలో వ్రాయగలిగేలా అనుమతిస్తుంది"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"కార్యాచరణలను పరిమాణం మార్చగలిగేలా నిర్బంధించు"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"మానిఫెస్ట్ విలువలతో సంబంధం లేకుండా అన్ని కార్యాచరణలను బహుళ విండోల్లో సరిపోయేటట్లు పరిమాణం మార్చగలిగేలా చేస్తుంది."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"స్వతంత్ర రూప విండోలను ప్రారంభించండి"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"ప్రయోగాత్మక స్వతంత్ర రూప విండోలకు మద్దతును ప్రారంభిస్తుంది."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"డెస్క్‌టాప్ బ్యాకప్ పాస్‌వర్డ్"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"డెస్క్‌టాప్ పూర్తి బ్యాకప్‌లు ప్రస్తుతం రక్షించబడలేదు"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"డెస్క్‌టాప్ పూర్తి బ్యాకప్‌ల కోసం పాస్‌వర్డ్‌ను మార్చడానికి లేదా తీసివేయడానికి తాకండి"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"స్వయంచాలకం"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"వెబ్ వీక్షణ అమలు"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"వెబ్ వీక్షణ అమలుని సెట్ చేయండి"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"ఎంచుకున్న వెబ్ వీక్షణ అమలు నిలిపివేయబడింది, కానీ ఉపయోగించడానికి తప్పనిసరిగా ప్రారంభించాల్సి ఉంటుంది, మీరు దీన్ని ప్రారంభించాలనుకుంటున్నారా?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"ఫైల్ గుప్తీకరణకు మార్చు"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"మార్చండి…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ఫైల్ ఇప్పటికే గుప్తీకరించబడింది"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"రంగు సవరణ"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - సుమారు <xliff:g id="TIME">%2$s</xliff:g> మిగిలి ఉంది"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACలో పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB ద్వారా పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - వైర్‌లెస్ నుండి పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"తెలియదు"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"ఛార్జ్ అవుతోంది"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACలో ఛార్జ్ అవుతోంది"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ద్వారా ఛార్జ్ అవుతోంది"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"వైర్‌లెస్‌ ద్వారా ఛార్జ్ అవుతోంది"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ఛార్జ్ కావడం లేదు"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ఛార్జ్ కావడం లేదు"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index de2f33d..241fd06 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"ให้สามารถเขียนแอปต่างๆ ไปยังที่เก็บภายนอกได้ โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"บังคับให้กิจกรรมปรับขนาดได้"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"ทำให้กิจกรรมทั้งหมดปรับขนาดได้สำหรับหน้าต่างหลายบาน โดยไม่คำนึงถึงค่าในไฟล์ Manifest"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"เปิดใช้หน้าต่างรูปแบบอิสระ"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"เปิดการสนับสนุนหน้าต่างรูปแบบอิสระแบบทดลอง"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"รหัสผ่านการสำรองข้อมูลในเดสก์ท็อป"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"การสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป ไม่ได้รับการป้องกันในขณะนี้"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"แตะเพื่อเปลี่ยนหรือลบรหัสผ่านสำหรับการสำรองข้อมูลเต็มรูปแบบในเดสก์ท็อป"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"อัตโนมัติ"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"การใช้งาน WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ตั้งค่าการใช้งาน WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"แปลงเป็นการเข้ารหัสไฟล์"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"แปลง…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"เข้ารหัสไฟล์แล้ว"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"การแก้สี"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"คุณลักษณะนี้เป็นแบบทดลองและอาจส่งผลต่อประสิทธิภาพการทำงาน"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลือประมาณ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็ม"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่านระบบไร้สาย"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"ไม่ทราบ"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"กำลังชาร์จไฟ AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"กำลังชาร์จผ่าน USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"กำลังชาร์จแบบไร้สาย"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"ไม่ได้ชาร์จ"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ไม่ได้ชาร์จ"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 231079b..d1a525b 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Mara-write na sa external storage ang anumang app, anuman ang manifest value"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Sapilitang gawing resizable ang mga aktibidad"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Gawing resizable para sa multi-window ang lahat ng aktibidad, anuman ang mga manifest value."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"I-enable ang mga freeform window"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Ine-enable ang suporta para sa mga pang-eksperimentong freeform window."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Password ng pag-backup ng desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Kasalukuyang hindi pinoprotektahan ang mga buong pag-backup ng desktop"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Pindutin upang baguhin o alisin ang password para sa mga buong pag-backup ng desktop"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Awtomatiko"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Pagpapatupad sa WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Itakda ang pagpapatupad sa WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"I-convert at gawing pag-encrypt ng file"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"I-convert..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Na-encrypt na ang file"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Pagtatama ng kulay"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ang feature na ito ay pinag-eeksperimentuhan at maaaring makaapekto sa pagganap."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - humigit kumulang <xliff:g id="TIME">%2$s</xliff:g> ang natitira"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno mula sa wireless"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Hindi Kilala"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Nagcha-charge"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nagcha-charge sa AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nagcha-charge sa USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Wireless nag-charge"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Hindi nagcha-charge"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hindi nagkakarga"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index c44d7f8..d4c211c 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Bildirilen değerlerden bağımsız olarak uygulamaları harici depolamaya yazmak için uygun hale getirir"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Etkinlikleri yeniden boyutlandırılabilmeye zorla"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifest değerlerinden bağımsız olarak, tüm etkinlikleri birden fazla pencerede yeniden boyutlandırılabilir hale getirir."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Serbest biçimli pencereleri etkinleştir"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Deneysel serbest biçimli pencereleri etkinleştirir."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Masaüstü yedekleme şifresi"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Masaüstü tam yedeklemeleri şu an korunmuyor"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Masaüstü tam yedeklemelerinin şifresini değiştirmek veya kaldırmak için dokunun"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Otomatik"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView kullanımı"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView kullanımını ayarla"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Dosya şifrelemeye dönüştür"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Dönüştür…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dosya şifreleme zaten uygulandı"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Renk düzeltme"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu özellik deneyseldir ve performansı etkileyebilir."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - yaklaşık <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - prize takılı, tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB üzerinden şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - kablosuzdan tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Bilinmiyor"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Şarj oluyor"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ile şarj oluyor"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ile şarj oluyor"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kablosuz şarj oluyor"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Şarj olmuyor"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Şarj etmiyor"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index d5dbe90..3e89cce 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Додатки можна записувати на зовнішню пам’ять незалежно від значень маніфесту"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Примусово масштабувати активність"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Активність масштабуватиметься на кілька вікон, незалежно від значень у файлі маніфесту."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Увімкнути вікна довільного формату"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Увімкнуться експериментальні вікна довільного формату."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Пароль резерв.копії на ПК"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Повні резервні копії на комп’ютері наразі не захищені"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Торкніться, щоб змінити чи видалити пароль для повного резервного копіювання на комп’ютер"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Автоматично"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Застосування WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Налаштувати застосування WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Вибране застосування WebView вимкнено. Увімкнути його?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Конвертувати в зашифрований файл"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Конвертація…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Уже конвертовано в зашифрований файл"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекція кольору"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Це експериментальна функція. Вона може вплинути на продуктивність."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – залишилось близько <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження з розетки"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження через USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного з бездротового зарядження"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Невідомо"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядж-ся"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Заряджання з розетки"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Заряджання через USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Заряджання без дроту"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряджається"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряджається"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 4c9c178..0a2adf7 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"‏manifest اقدار سے قطع نظر، کسی بھی ایپ کو بیرونی اسٹوریج پر لکھے جانے کا اہل بناتا ہے"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"سرگرمیوں کو ری سائز ایبل بنائیں"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"‏manifest اقدار سے قطع نظر، ملٹی ونڈو کیلئے تمام سرگرمیوں کو ری سائز ایبل بناتا ہے۔"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"‏freeform ونڈوز فعال کریں"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"‏تجرباتی freeform ونڈوز کے لئے سپورٹ فعال کرتا ہے۔"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"ڈیسک ٹاپ کا بیک اپ پاس ورڈ"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"ڈیسک ٹاپ کے مکمل بیک اپس فی الحال محفوظ کیے ہوئے نہیں ہیں"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"ڈیسک ٹاپ کے مکمل بیک اپس کیلئے پاس ورڈ کو تبدیل کرنے یا ہٹانے کیلئے ٹچ کریں"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"خودکار"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"‏WebView کا نفاذ"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"‏WebView کا نفاذ سیٹ کریں"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"فائل مرموز کاری میں بدلیں"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"بدلیں…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"فائل پہلے ہی مرموز شدہ ہے"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"رنگ کی اصلاح"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"یہ خصوصیت تجرباتی ہے اور اس کی وجہ سے کاکردگی متاثر ہو سکتی ہے۔"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎ - تقریبا <xliff:g id="TIME">%2$s</xliff:g> باقی"</string>
+    <string name="power_charging" msgid="1779532561355864267">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>‎ پورا ہونے تک"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC‎ پر پورا ہونے تک"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB‎ پر پورا ہونے تک"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>‎ وائرلیس سے پورا ہونے تک"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"نامعلوم"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"چارج ہو رہا ہے"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"‏AC پر چارج ہو رہی ہے"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏‫USB پر چارج ہورہی ہے"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"وائرلیس چارجنگ"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"چارج نہیں ہو رہا ہے"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"چارج نہیں ہو رہا ہے"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index d138a28..13494a5 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Manifest qiymatidan qat’i nazar istalgan ilovani tashqi xotiraga saqlash imkonini beradi"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Harakatlarni moslashuvchan o‘lchamga keltirish"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Manifest qiymatidan qat’i nazar barcha harakatlarni ko‘p oynali rejimga moslashtiradi."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Erkin shakldagi oynalarni yoqish"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Tajribaviy erkin shakldagi oynalar ta’minotini yoqadi"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Zaxira nusxa uchun parol"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Kompyuterdagi zaxira nusxalar hozirgi vaqtda himoyalanmagan"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Ish stoli to\'liq zaxira nusxalari parolini o‘zgartirish yoki o‘chirish uchun bu yerni bosing."</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Avtomatik"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ta’minotchisi"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ta’minotchisini sozlash"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Faylli shifrga o‘girish"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"O‘girish…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl allaqachon shifrlangan"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Rangni tuzatish"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya tajribaviy bo‘lib, u qurilma unumdorligiga ta’sir qilishi mumkin."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – taxminan <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, to‘lguncha"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, o‘zgaruvchan tok orqali to‘lguncha"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, USB orqali to‘lguncha"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, simsiz quvvatlash orqali to‘lguncha"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Noma’lum"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Quvvat olmoqda"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Quvvat olmoqda (AC)"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Quvvat olmoqda (USB)"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Simsiz quvvat olmoqda"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Quvvatlantirilmayapti"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Quvvatlanmayapti"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 7ab8b02..81b87da 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Giúp ứng dụng bất kỳ đủ điều kiện được ghi vào bộ nhớ ngoài bất kể giá trị tệp kê khai là gì"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Buộc các hoạt động có thể thay đổi kích thước"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Giúp tất cả hoạt động có thể thay đổi kích thước cho nhiều cửa sổ bất kể giá trị tệp kê khai là gì."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Bật cửa sổ dạng tự do"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Bật tính năng hỗ trợ cửa sổ dạng tự do thử nghiệm."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Mật khẩu sao lưu của máy tính"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Sao lưu toàn bộ máy tính hiện không được bảo vệ"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Chạm để thay đổi hoặc xóa mật khẩu dành cho bộ sao lưu toàn bộ tới máy tính"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Tự động"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Triển khai WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Đặt triển khai WebView"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Chuyển đổi sang mã hóa tệp"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Chuyển đổi..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Đã mã hóa tệp"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Sửa màu"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tính năng này là tính năng thử nghiệm và có thể ảnh hưởng đến hoạt động."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - còn khoảng <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy khi cắm vào nguồn AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy qua USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy từ không dây"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Sạc trên AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Sạc qua USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Sạc không dây"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Hiện không sạc"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hiện không sạc"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Đầy"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 509ff88..5ffcc0c 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允许将任何应用写入外部存储设备(无论清单值是什么)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"强制将活动设为可调整大小"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"将所有活动设为可配合多窗口环境调整大小(无论清单值是什么)。"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"启用可自由调整的窗口"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"启用可自由调整的窗口这一实验性功能。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"桌面备份密码"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"桌面完整备份当前未设置密码保护"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"触摸可更改或删除用于桌面完整备份的密码"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"自动"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 实现"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"设置 WebView 实现"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"转换为文件加密"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"转换…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"文件已加密"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"此功能为实验性功能,可能会影响性能。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还可用大约<xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(交流电充电)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(USB充电)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(无线充电)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"正在充电"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在通过交流电源充电"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在通过USB充电"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在无线充电"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"未在充电"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未在充电"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index f323667..8eedf6d 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允許將所有應用程式寫入到外部儲存完間 (所有資訊清單值)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"強制可變更活動尺寸"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"在任何資訊清單值下,允許為多個視窗變更所有活動的尺寸。"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"啟用自由形態視窗"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"啟用實驗版自由形態視窗的支援功能。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"桌面電腦備份密碼"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"桌上電腦的完整備份目前未受保護"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"輕觸即可更改或移除桌上電腦完整備份的密碼"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 設置"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"設定 WebView 設置"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"轉換為檔案加密"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"轉換…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"已加密檔案"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會影響效能。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 尚餘大約 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過插頭充電)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過 USB 充電)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (無線充電)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 充電"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未開始充電"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index c6ad832..10d2e01f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"將所有活動設為可配合多重視窗環境調整大小 (無論資訊清單值為何)。"</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"啟用自由形式視窗"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"啟用實驗版自由形式視窗的支援功能。"</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"電腦備份密碼"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"電腦完整備份目前未受保護"</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"輕觸即可變更或移除電腦完整備份的密碼"</string>
@@ -275,6 +277,8 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 實作"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"設定 WebView 實作"</string>
+    <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+    <skip />
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"轉換成檔案加密"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"轉換..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"已將檔案加密"</string>
@@ -291,4 +295,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會對效能造成影響。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 大約還剩 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (AC)"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (USB)"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (無線充電)"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 變壓器充電"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"非充電中"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index cb3670f..f42abbc 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -247,6 +247,8 @@
     <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Yenza noma uluphi uhlelo lokusebenza lifaneleke ukuthi libhalwe kusitoreji sangaphandle, ngaphandle kwamavelu we-manifest"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"Imisebenzi yamandla izonikezwa usayizi omusha"</string>
     <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Yenza yonke imisebenzi ibe nosayizi abasha kuwindi lokuningi, ngokunganaki amanani we-manifest."</string>
+    <string name="enable_freeform_support" msgid="1461893351278940416">"Nika amandla amawindi e-freeform"</string>
+    <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Inika amandla usekelo lwamawindi okuhlola e-freeform."</string>
     <string name="local_backup_password_title" msgid="3860471654439418822">"Iphasiwedi yokusekela ngokulondoloza ye-Desktop"</string>
     <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Ukusekela ngokulondoloza okugcwele kwe-Desktop akuvikelekile okwamanje."</string>
     <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Khetha ukushintsha noma ukususa iphasiwedi yokwenziwa kwezipele ngokugcwele kwideskithophu"</string>
@@ -275,6 +277,7 @@
     <string name="night_mode_auto" msgid="7508348175804304327">"Okuzenzakalelayo"</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"Ukufakwa ke-WebView"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Sesba ukufakwa kwe-WebView"</string>
+    <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Ukusetshenziswa kwe-WebView okukhethiwe kukhutshaziwe, futhi kuzomele kunikwe amandla ukuze kusetshenziswe, ingabe ufisa ukukunika amandla?"</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"Phendulisela ekubethelweni kwefayela"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Iyaphendulela..."</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sekuvele kubethelwe ngefayela"</string>
@@ -291,4 +294,18 @@
     <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Ukulungiswa kombala"</string>
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Lesi sici esesilingo futhi singathinta ukusebenza."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - isilinganiso esingu-<xliff:g id="TIME">%2$s</xliff:g> esisele"</string>
+    <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+    <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale"</string>
+    <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ku-AC"</string>
+    <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ngaphezulu kwe-USB"</string>
+    <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale kusukela kokungenantambo"</string>
+    <string name="battery_info_status_unknown" msgid="196130600938058547">"Akwaziwa"</string>
+    <string name="battery_info_status_charging" msgid="1705179948350365604">"Iyashaja"</string>
+    <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Iyashaja ku-AC"</string>
+    <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Iyashaja ngaphezulu kwe-USB"</string>
+    <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Iyashaja ngaphandle kwentambo"</string>
+    <string name="battery_info_status_discharging" msgid="310932812698268588">"Ayishaji"</string>
+    <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ayishaji"</string>
+    <string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 2a4efbd..1bce7f9 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -128,7 +128,7 @@
         <item>16777216</item>
     </string-array>
 
-    <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=30]-->
+    <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=50]-->
     <string-array name="select_logd_size_summaries" >
         <item>Off</item>
         <item>64K per log buffer</item>
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
new file mode 100644
index 0000000..46267a2
--- /dev/null
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <declare-styleable name="RestrictedPreference">
+        <attr name="userRestriction" format="string"/>
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
new file mode 100644
index 0000000..c090468
--- /dev/null
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <color name="disabled_text_color">#66000000</color> <!-- 38% black -->
+</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index d7c78f6..9a1d6a4 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -31,4 +31,8 @@
     <dimen name="user_spinner_padding">4dp</dimen>
     <dimen name="user_spinner_padding_sides">20dp</dimen>
     <dimen name="user_spinner_item_height">56dp</dimen>
+
+    <!-- Lock icon for preferences locked by admin -->
+    <dimen name="restricted_lock_icon_size">16dp</dimen>
+    <dimen name="restricted_lock_icon_padding">4dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7e22881..6dfa9ad 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -624,6 +624,11 @@
     <!-- UI debug setting: force allow on external summary [CHAR LIMIT=150] -->
     <string name="force_resizable_activities_summary">Makes all activities resizable for multi-window, regardless of manifest values.</string>
 
+    <!-- UI debug setting: enable freeform window support [CHAR LIMIT=50] -->
+    <string name="enable_freeform_support">Enable freeform windows</string>
+    <!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] -->
+    <string name="enable_freeform_support_summary">Enables support for experimental freeform windows.</string>
+
     <!-- Local (desktop) backup password menu title [CHAR LIMIT=25] -->
     <string name="local_backup_password_title">Desktop backup password</string>
     <!-- Summary text of the "local backup password" setting when the user has not supplied a password -->
@@ -679,6 +684,8 @@
     <string name="select_webview_provider_title">WebView implementation</string>
     <!-- Developer settings: select WebView provider dialog title -->
     <string name="select_webview_provider_dialog_title">Set WebView implementation</string>
+    <!-- Developer settings: confirmation dialog text for the WebView provider selection dialog -->
+    <string name="select_webview_provider_confirmation_text">The chosen WebView implementation is disabled, and must be enabled to be used, do you wish to enable it?</string>
 
     <!-- Developer settings screen, convert userdata to file encryption option name -->
     <string name="convert_to_file_encryption">Convert to file encryption</string>
@@ -721,4 +728,41 @@
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+    <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g>
+        - approx. <xliff:g id="time">%2$s</xliff:g> left</string>
+
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
+    <string name="power_charging"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="state">%2$s</xliff:g></string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_ac"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full on AC</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_usb"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full over USB</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_wireless"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full from wireless</string>
+
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_unknown">Unknown</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging from an unknown source.  -->
+    <string name="battery_info_status_charging">Charging</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging on AC.  -->
+    <string name="battery_info_status_charging_ac">Charging on AC</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging over USB.  -->
+    <string name="battery_info_status_charging_usb">Charging over USB</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging over a wireless connection.  -->
+    <string name="battery_info_status_charging_wireless">Charging wirelessly</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_discharging">Not charging</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_not_charging">Not charging</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_full">Full</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
new file mode 100644
index 0000000..d81bdeb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
@@ -0,0 +1,107 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.format.Formatter;
+import com.android.internal.os.BatteryStatsHelper;
+
+public class BatteryInfo {
+
+    public String mChargeLabelString;
+    public int mBatteryLevel;
+    public boolean mDischarging = true;
+    public long remainingTimeUs = 0;
+
+    public interface Callback {
+        void onBatteryInfoLoaded(BatteryInfo info);
+    }
+
+    public static void getBatteryInfo(final Context context, final Callback callback) {
+        new AsyncTask<Void, Void, BatteryStats>() {
+            @Override
+            protected BatteryStats doInBackground(Void... params) {
+                BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, true);
+                statsHelper.create((Bundle) null);
+                return statsHelper.getStats();
+            }
+
+            @Override
+            protected void onPostExecute(BatteryStats batteryStats) {
+                final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
+                Intent batteryBroadcast = context.registerReceiver(null,
+                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+                BatteryInfo batteryInfo = BatteryInfo.getBatteryInfo(context,
+                        batteryBroadcast, batteryStats, elapsedRealtimeUs);
+                callback.onBatteryInfoLoaded(batteryInfo);
+            }
+        }.execute();
+    }
+
+    public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
+            BatteryStats stats, long elapsedRealtimeUs) {
+        BatteryInfo info = new BatteryInfo();
+        info.mBatteryLevel = Utils.getBatteryLevel(batteryBroadcast);
+        String batteryPercentString = Utils.formatPercentage(info.mBatteryLevel);
+        if (batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
+            final long drainTime = stats.computeBatteryTimeRemaining(elapsedRealtimeUs);
+            if (drainTime > 0) {
+                info.remainingTimeUs = drainTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        drainTime / 1000);
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_discharging_duration, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = batteryPercentString;
+            }
+        } else {
+            final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
+            final String statusLabel = Utils.getBatteryStatus(
+                    context.getResources(), batteryBroadcast);
+            final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
+                    BatteryManager.BATTERY_STATUS_UNKNOWN);
+            if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
+                info.mDischarging = false;
+                info.remainingTimeUs = chargeTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        chargeTime / 1000);
+                int plugType = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+                int resId;
+                if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                    resId = R.string.power_charging_duration_ac;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                    resId = R.string.power_charging_duration_usb;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                    resId = R.string.power_charging_duration_wireless;
+                } else {
+                    resId = R.string.power_charging_duration;
+                }
+                info.mChargeLabelString = context.getResources().getString(
+                        resId, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_charging, batteryPercentString, statusLabel);
+            }
+        }
+        return info;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
new file mode 100644
index 0000000..c2f885d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -0,0 +1,153 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.support.v7.preference.DropDownPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+public class RestrictedDropDownPreference extends DropDownPreference {
+    private Spinner mSpinner;
+    private final Drawable mRestrictedPadlock;
+    private final int mRestrictedPadlockPadding;
+    private List<RestrictedItem> mRestrictedItems = new ArrayList<>();
+
+    public RestrictedDropDownPreference(Context context) {
+        this(context, null);
+    }
+
+    public RestrictedDropDownPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context);
+        mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_padding);
+    }
+
+    private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
+            if (position >= 0) {
+                String value = getEntryValues()[position].toString();
+                RestrictedItem item = getRestrictedItemForEntryValue(value);
+                if (item != null) {
+                    RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
+                            item.enforcedAdmin);
+                    mSpinner.setSelection(findIndexOfValue(getValue()));
+                } else if (!value.equals(getValue()) && callChangeListener(value)) {
+                    setValue(value);
+                }
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // noop
+        }
+    };
+
+    @Override
+    protected ArrayAdapter createAdapter() {
+        return new RestrictedArrayItemAdapter(getContext());
+    }
+
+    @Override
+    public void setValue(String value) {
+        if (getRestrictedItemForEntryValue(value) != null) {
+            return;
+        }
+        super.setValue(value);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+        mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner);
+        mSpinner.setOnItemSelectedListener(mItemSelectedListener);
+    }
+
+    private class RestrictedArrayItemAdapter extends ArrayAdapter<String> {
+        public RestrictedArrayItemAdapter(Context context) {
+            super(context, R.layout.spinner_dropdown_restricted_item);
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) super.getView(position, convertView, parent);
+            CharSequence entry = getItem(position);
+            boolean isEntryRestricted = isRestrictedForEntry(entry);
+            RestrictedLockUtils.setTextViewPadlock(getContext(), view, isEntryRestricted);
+            view.setEnabled(!isEntryRestricted);
+            return view;
+        }
+    }
+
+    private boolean isRestrictedForEntry(CharSequence entry) {
+        if (entry == null) {
+            return false;
+        }
+        for (RestrictedItem item : mRestrictedItems) {
+            if (entry.equals(item.entry)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) {
+        if (entryValue == null) {
+            return null;
+        }
+        for (RestrictedItem item : mRestrictedItems) {
+            if (entryValue.equals(item.entryValue)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    public void addRestrictedItem(RestrictedItem item) {
+        mRestrictedItems.add(item);
+    }
+
+    public static class RestrictedItem {
+        public CharSequence entry;
+        public CharSequence entryValue;
+        public EnforcedAdmin enforcedAdmin;
+
+        public RestrictedItem(CharSequence entry, CharSequence entryValue,
+                EnforcedAdmin enforcedAdmin) {
+            this.entry = entry;
+            this.entryValue = entryValue;
+            this.enforcedAdmin = enforcedAdmin;
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
new file mode 100644
index 0000000..e63130d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.text.style.ImageSpan;
+
+/**
+ * An extension of ImageSpan which adds a padding before the image.
+ */
+public class RestrictedLockImageSpan extends ImageSpan {
+    private Context mContext;
+    private final float mExtraPadding;
+    private final Drawable mRestrictedPadlock;
+
+    public RestrictedLockImageSpan(Context context) {
+        // we are overriding getDrawable, so passing null to super class here.
+        super((Drawable) null);
+
+        mContext = context;
+        mExtraPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_padding);
+        mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
+    }
+
+    @Override
+    public Drawable getDrawable() {
+        return mRestrictedPadlock;
+    }
+
+    @Override
+    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
+            int bottom, Paint paint) {
+        Drawable drawable = getDrawable();
+        canvas.save();
+
+        // Add extra padding before the padlock.
+        float transX = x + mExtraPadding;
+        float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent;
+
+        canvas.translate(transX, transY);
+        drawable.draw(canvas);
+        canvas.restore();
+    }
+
+    @Override
+    public int getSize(Paint paint, CharSequence text, int start, int end,
+            Paint.FontMetricsInt fontMetrics) {
+        int size = super.getSize(paint, text, start, end, fontMetrics);
+        size += 2 * mExtraPadding;
+        return size;
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
new file mode 100644
index 0000000..2706e25
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -0,0 +1,266 @@
+/*
+ * 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;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.Spanned;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.ImageSpan;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Utility class to host methods usable in adding a restricted padlock icon and showing admin
+ * support message dialog.
+ */
+public class RestrictedLockUtils {
+    /**
+     * @return drawables for displaying with settings that are locked by a device admin.
+     */
+    public static Drawable getRestrictedPadlock(Context context) {
+        Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline);
+        final int iconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_size);
+        restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
+        return restrictedPadlock;
+    }
+
+    /**
+     * Checks if a restriction is enforced on a user and returns the enforced admin and
+     * admin userId.
+     *
+     * @param userRestriction Restriction to check
+     * @param userId User which we need to check if restriction is enforced on.
+     * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
+     * {@code null} If the restriction is not set. If the restriction is set by both device owner
+     * and profile owner, then the admin will be set to {@code null} and userId to
+     * {@link UserHandle#USER_NULL}.
+     */
+    public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
+            String userRestriction, int userId) {
+        DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
+        int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
+        boolean enforcedByDeviceOwner = false;
+        if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) {
+            Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId);
+            if (enforcedRestrictions != null
+                    && enforcedRestrictions.getBoolean(userRestriction, false)) {
+                enforcedByDeviceOwner = true;
+            }
+        }
+
+        ComponentName profileOwner = null;
+        boolean enforcedByProfileOwner = false;
+        if (userId != UserHandle.USER_NULL) {
+            profileOwner = dpm.getProfileOwnerAsUser(userId);
+            if (profileOwner != null) {
+                Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId);
+                if (enforcedRestrictions != null
+                        && enforcedRestrictions.getBoolean(userRestriction, false)) {
+                    enforcedByProfileOwner = true;
+                }
+            }
+        }
+
+        if (!enforcedByDeviceOwner && !enforcedByProfileOwner) {
+            return null;
+        }
+
+        EnforcedAdmin admin = null;
+        if (enforcedByDeviceOwner && enforcedByProfileOwner) {
+            admin = new EnforcedAdmin();
+        } else if (enforcedByDeviceOwner) {
+            admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId);
+        } else {
+            admin = new EnforcedAdmin(profileOwner, userId);
+        }
+        return admin;
+    }
+
+    /**
+     * Checks if lock screen notification features are disabled by policy. This should be
+     * only used for keyguard notification features but not the keyguard features
+     * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user
+     * as it won't work for that case.
+     *
+     * @param keyguardNotificationFeatures Could be any of notification features that can be
+     * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
+     * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
+     * {@code null} If the notification features are not disabled. If the restriction is set by
+     * multiple admins, then the admin will be set to {@code null} and userId to
+     * {@link UserHandle#USER_NULL}.
+     */
+    public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context,
+            int keyguardNotificationFeatures) {
+        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        boolean isDisabledByMultipleAdmins = false;
+        ComponentName adminComponent = null;
+        List<ComponentName> admins = dpm.getActiveAdmins();
+        int disabledKeyguardFeatures;
+        for (ComponentName admin : admins) {
+            disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin);
+            if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) {
+                if (adminComponent == null) {
+                    adminComponent = admin;
+                } else {
+                    isDisabledByMultipleAdmins = true;
+                    break;
+                }
+            }
+        }
+        EnforcedAdmin enforcedAdmin = null;
+        if (adminComponent != null) {
+            if (!isDisabledByMultipleAdmins) {
+                enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
+            } else {
+                enforcedAdmin = new EnforcedAdmin();
+            }
+        }
+        return enforcedAdmin;
+    }
+
+    /**
+     * Set the menu item as disabled by admin by adding a restricted padlock at the end of the
+     * text and set the click listener which will send an intent to show the admin support details
+     * dialog.
+     */
+    public static void setMenuItemAsDisabledByAdmin(final Context context,
+            final MenuItem item, final EnforcedAdmin admin) {
+        SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle());
+        removeExistingRestrictedSpans(sb);
+
+        final int disabledColor = context.getColor(R.color.disabled_text_color);
+        sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        ImageSpan image = new RestrictedLockImageSpan(context);
+        sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        item.setTitle(sb);
+
+        item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override
+            public boolean onMenuItemClick(MenuItem item) {
+                sendShowAdminSupportDetailsIntent(context, admin);
+                return true;
+            }
+        });
+    }
+
+    private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) {
+        final int length = sb.length();
+        RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length,
+                RestrictedLockImageSpan.class);
+        for (ImageSpan span : imageSpans) {
+            sb.removeSpan(span);
+        }
+        ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class);
+        for (ForegroundColorSpan span : colorSpans) {
+            sb.removeSpan(span);
+        }
+    }
+
+    /**
+     * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}.
+     */
+    public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
+        final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
+        int adminUserId = UserHandle.myUserId();
+        if (admin.userId != UserHandle.USER_NULL) {
+            adminUserId = admin.userId;
+        }
+        context.startActivityAsUser(intent, new UserHandle(adminUserId));
+    }
+
+    public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
+        final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+        if (admin != null) {
+            if (admin.component != null) {
+                intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
+            }
+            int adminUserId = UserHandle.myUserId();
+            if (admin.userId != UserHandle.USER_NULL) {
+                adminUserId = admin.userId;
+            }
+            intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
+        }
+        return intent;
+    }
+
+    public static void setTextViewPadlock(Context context,
+            TextView textView, boolean showPadlock) {
+        final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
+        removeExistingRestrictedSpans(sb);
+        if (showPadlock) {
+            final ImageSpan image = new RestrictedLockImageSpan(context);
+            sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        textView.setText(sb);
+    }
+
+    public static class EnforcedAdmin {
+        public ComponentName component = null;
+        public int userId = UserHandle.USER_NULL;
+
+        public EnforcedAdmin(ComponentName component, int userId) {
+            this.component = component;
+            this.userId = userId;
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            if (object == this) return true;
+            if (!(object instanceof EnforcedAdmin)) return false;
+            EnforcedAdmin other = (EnforcedAdmin) object;
+            if (userId != other.userId) {
+                return false;
+            }
+            if ((component == null && other == null) ||
+                    (component != null && component.equals(other))) {
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}";
+        }
+
+        public void copyTo(EnforcedAdmin other) {
+            if (other == null) {
+                other = new EnforcedAdmin();
+            }
+            other.component = component;
+            other.userId = userId;
+        }
+
+        public EnforcedAdmin() {}
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
new file mode 100644
index 0000000..13a46d0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Preference class that supports being disabled by a user restriction
+ * set by a device admin.
+ */
+public class RestrictedPreference extends Preference {
+    RestrictedPreferenceHelper mHelper;
+
+    public RestrictedPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+    }
+
+    public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RestrictedPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+                android.R.attr.preferenceStyle));
+    }
+
+    public RestrictedPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        mHelper.onBindViewHolder(holder);
+    }
+
+    @Override
+    public void performClick() {
+        if (!mHelper.performClick()) {
+            super.performClick();
+        }
+    }
+
+    @Override
+    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+        mHelper.onAttachedToHierarchy();
+        super.onAttachedToHierarchy(preferenceManager);
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (enabled && isDisabledByAdmin()) {
+            mHelper.setDisabledByAdmin(null);
+            return;
+        }
+        super.setEnabled(enabled);
+    }
+
+    public void setDisabledByAdmin(EnforcedAdmin admin) {
+        if (mHelper.setDisabledByAdmin(admin)) {
+            notifyChanged();
+        }
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mHelper.isDisabledByAdmin();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
new file mode 100644
index 0000000..06aba96
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.Spanned;
+import android.text.SpannableStringBuilder;
+import android.text.style.ImageSpan;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Helper class for managing settings preferences that can be disabled
+ * by device admins via user restrictions.
+ */
+public class RestrictedPreferenceHelper {
+    private final Context mContext;
+    private final Preference mPreference;
+    private final Drawable mRestrictedPadlock;
+    private final int mRestrictedPadlockPadding;
+
+    private boolean mDisabledByAdmin;
+    private EnforcedAdmin mEnforcedAdmin;
+    private String mAttrUserRestriction = null;
+
+    public RestrictedPreferenceHelper(Context context, Preference preference,
+            AttributeSet attrs) {
+        mContext = context;
+        mPreference = preference;
+
+        mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
+        mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.restricted_lock_icon_padding);
+
+        if (attrs != null) {
+            final TypedArray attributes = context.obtainStyledAttributes(attrs,
+                    R.styleable.RestrictedPreference);
+            final TypedValue userRestriction =
+                    attributes.peekValue(R.styleable.RestrictedPreference_userRestriction);
+            CharSequence data = null;
+            if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) {
+                if (userRestriction.resourceId != 0) {
+                    data = context.getText(userRestriction.resourceId);
+                } else {
+                    data = userRestriction.string;
+                }
+            }
+            mAttrUserRestriction = data == null ? null : data.toString();
+        }
+    }
+
+    /**
+     * Modify PreferenceViewHolder to add padlock if restriction is disabled.
+     */
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
+        if (titleView != null) {
+            RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin);
+            if (mDisabledByAdmin) {
+                holder.itemView.setEnabled(true);
+            }
+        }
+    }
+
+    /**
+     * Check if the preference is disabled if so handle the click by informing the user.
+     *
+     * @return true if the method handled the click.
+     */
+    public boolean performClick() {
+        if (mDisabledByAdmin) {
+            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Disable / enable if we have been passed the restriction in the xml.
+     */
+    public void onAttachedToHierarchy() {
+        if (mAttrUserRestriction != null) {
+            checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId());
+        }
+    }
+
+    /**
+     * Set the user restriction that is used to disable this preference.
+     *
+     * @param userRestriction constant from {@link android.os.UserManager}
+     * @param userId user to check the restriction for.
+     */
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+                userRestriction, userId);
+        setDisabledByAdmin(admin);
+    }
+
+    /**
+     * Disable this preference based on the enforce admin.
+     *
+     * @param EnforcedAdmin Details of the admin who enforced the restriction. If it
+     * is {@code null}, then this preference will be enabled. Otherwise, it will be disabled.
+     * @return true if the disabled state was changed.
+     */
+    public boolean setDisabledByAdmin(EnforcedAdmin admin) {
+        final boolean disabled = (admin != null ? true : false);
+        mEnforcedAdmin = (disabled ? admin : null);
+        if (mDisabledByAdmin != disabled) {
+            mDisabledByAdmin = disabled;
+            mPreference.setEnabled(!disabled);
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mDisabledByAdmin;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
new file mode 100644
index 0000000..84e2bff
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -0,0 +1,100 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v14.preference.SwitchPreference;
+import android.util.AttributeSet;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Version of SwitchPreference that can be disabled by a device admin
+ * using a user restriction.
+ */
+public class RestrictedSwitchPreference extends SwitchPreference {
+    RestrictedPreferenceHelper mHelper;
+
+    public RestrictedSwitchPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+    }
+
+    public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public RestrictedSwitchPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
+                android.R.attr.switchPreferenceStyle));
+    }
+
+    public RestrictedSwitchPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        mHelper.onBindViewHolder(holder);
+    }
+
+    @Override
+    public void performClick() {
+        if (!mHelper.performClick()) {
+            super.performClick();
+        }
+    }
+
+    @Override
+    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+        mHelper.onAttachedToHierarchy();
+        super.onAttachedToHierarchy(preferenceManager);
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+    }
+
+    public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+        mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (enabled && isDisabledByAdmin()) {
+            mHelper.setDisabledByAdmin(null);
+            return;
+        }
+        super.setEnabled(enabled);
+    }
+
+    public void setDisabledByAdmin(EnforcedAdmin admin) {
+        if (mHelper.setDisabledByAdmin(admin)) {
+            notifyChanged();
+        }
+    }
+
+    public boolean isDisabledByAdmin() {
+        return mHelper.isDisabledByAdmin();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
new file mode 100644
index 0000000..58a477e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
@@ -0,0 +1,177 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Xml;
+import android.view.InflateException;
+import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.drawer.TileUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SuggestionParser {
+
+    private static final String TAG = "SuggestionParser";
+
+    private final Context mContext;
+    private final List<SuggestionCategory> mSuggestionList;
+    private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>();
+
+    public SuggestionParser(Context context, int orderXml) {
+        mContext = context;
+        mSuggestionList = (List<SuggestionCategory>) new SuggestionOrderInflater(mContext)
+                .parse(orderXml);
+    }
+
+    public List<Tile> getSuggestions() {
+        List<Tile> suggestions = new ArrayList<>();
+        final int N = mSuggestionList.size();
+        for (int i = 0; i < N; i++) {
+            readSuggestions(mSuggestionList.get(i), suggestions);
+        }
+        return suggestions;
+    }
+
+    private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) {
+        int countBefore = suggestions.size();
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(category.category);
+        if (category.pkg != null) {
+            intent.setPackage(category.pkg);
+        }
+        TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
+                addCache, null, suggestions, true, false);
+        if (!category.multiple && suggestions.size() > (countBefore + 1)) {
+            // If there are too many, remove them all and only re-add the one with the highest
+            // priority.
+            Tile item = suggestions.remove(suggestions.size() - 1);
+            while (suggestions.size() > countBefore) {
+                Tile last = suggestions.remove(suggestions.size() - 1);
+                if (last.priority > item.priority) {
+                    item = last;
+                }
+            }
+            suggestions.add(item);
+        }
+    }
+
+    private static class SuggestionCategory {
+        public String category;
+        public String pkg;
+        public boolean multiple;
+    }
+
+    private static class SuggestionOrderInflater {
+        private static final String TAG_LIST = "optional-steps";
+        private static final String TAG_ITEM = "step";
+
+        private static final String ATTR_CATEGORY = "category";
+        private static final String ATTR_PACKAGE = "package";
+        private static final String ATTR_MULTIPLE = "multiple";
+
+        private final Context mContext;
+
+        public SuggestionOrderInflater(Context context) {
+            mContext = context;
+        }
+
+        public Object parse(int resource) {
+            XmlPullParser parser = mContext.getResources().getXml(resource);
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            try {
+                // Look for the root node.
+                int type;
+                do {
+                    type = parser.next();
+                } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
+
+                if (type != XmlPullParser.START_TAG) {
+                    throw new InflateException(parser.getPositionDescription()
+                            + ": No start tag found!");
+                }
+
+                // Temp is the root that was found in the xml
+                Object xmlRoot = onCreateItem(parser.getName(), attrs);
+
+                // Inflate all children under temp
+                rParse(parser, xmlRoot, attrs);
+                return xmlRoot;
+            } catch (XmlPullParserException | IOException e) {
+                Log.w(TAG, "Problem parser resource " + resource, e);
+                return null;
+            }
+        }
+
+        /**
+         * Recursive method used to descend down the xml hierarchy and instantiate
+         * items, instantiate their children.
+         */
+        private void rParse(XmlPullParser parser, Object parent, final AttributeSet attrs)
+                throws XmlPullParserException, IOException {
+            final int depth = parser.getDepth();
+
+            int type;
+            while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                    parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+
+                final String name = parser.getName();
+
+                Object item = onCreateItem(name, attrs);
+                onAddChildItem(parent, item);
+                rParse(parser, item, attrs);
+            }
+        }
+
+        protected void onAddChildItem(Object parent, Object child) {
+            if (parent instanceof List<?> && child instanceof SuggestionCategory) {
+                ((List<SuggestionCategory>) parent).add((SuggestionCategory) child);
+            } else {
+                throw new IllegalArgumentException("Parent was not a list");
+            }
+        }
+
+        protected Object onCreateItem(String name, AttributeSet attrs) {
+            if (name.equals(TAG_LIST)) {
+                return new ArrayList<SuggestionCategory>();
+            } else if (name.equals(TAG_ITEM)) {
+                SuggestionCategory category = new SuggestionCategory();
+                category.category = attrs.getAttributeValue(null, ATTR_CATEGORY);
+                category.pkg = attrs.getAttributeValue(null, ATTR_PACKAGE);
+                String multiple = attrs.getAttributeValue(null, ATTR_MULTIPLE);
+                category.multiple = !TextUtils.isEmpty(multiple) && Boolean.parseBoolean(multiple);
+                return category;
+            } else {
+                throw new IllegalArgumentException("Unknown item " + name);
+            }
+        }
+    }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 621a09cd..fa2226d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,17 +1,25 @@
 package com.android.settingslib;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
+import android.content.pm.Signature;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
+import android.os.BatteryManager;
 import android.os.UserManager;
-
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 
-public final class Utils {
+import java.text.NumberFormat;
+
+public class Utils {
 
     /**
      * Return string resource that best describes combination of tethering
@@ -81,4 +89,86 @@
         return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
                 UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
     }
+
+    /** Formats the ratio of amount/total as a percentage. */
+    public static String formatPercentage(long amount, long total) {
+        return formatPercentage(((double) amount) / total);
+    }
+
+    /** Formats an integer from 0..100 as a percentage. */
+    public static String formatPercentage(int percentage) {
+        return formatPercentage(((double) percentage) / 100.0);
+    }
+
+    /** Formats a double from 0.0..1.0 as a percentage. */
+    private static String formatPercentage(double percentage) {
+      return NumberFormat.getPercentInstance().format(percentage);
+    }
+
+    public static int getBatteryLevel(Intent batteryChangedIntent) {
+        int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+        int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
+        return (level * 100) / scale;
+    }
+
+    public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
+        final Intent intent = batteryChangedIntent;
+
+        int plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+                BatteryManager.BATTERY_STATUS_UNKNOWN);
+        String statusString;
+        if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+            int resId;
+            if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                resId = R.string.battery_info_status_charging_ac;
+            } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                resId = R.string.battery_info_status_charging_usb;
+            } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                resId = R.string.battery_info_status_charging_wireless;
+            } else {
+                resId = R.string.battery_info_status_charging;
+            }
+            statusString = res.getString(resId);
+        } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+            statusString = res.getString(R.string.battery_info_status_discharging);
+        } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+            statusString = res.getString(R.string.battery_info_status_not_charging);
+        } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+            statusString = res.getString(R.string.battery_info_status_full);
+        } else {
+            statusString = res.getString(R.string.battery_info_status_unknown);
+        }
+
+        return statusString;
+    }
+
+    /**
+     * Determine whether a package is a "system package", in which case certain things (like
+     * disabling notifications or disabling the package altogether) should be disallowed.
+     */
+    public static boolean isSystemPackage(PackageManager pm, PackageInfo pkg) {
+        if (sSystemSignature == null) {
+            sSystemSignature = new Signature[]{ getSystemSignature(pm) };
+        }
+        return sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg));
+    }
+
+    private static Signature[] sSystemSignature;
+
+    private static Signature getFirstSignature(PackageInfo pkg) {
+        if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
+            return pkg.signatures[0];
+        }
+        return null;
+    }
+
+    private static Signature getSystemSignature(PackageManager pm) {
+        try {
+            final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
+            return getFirstSignature(sys);
+        } catch (NameNotFoundException e) {
+        }
+        return null;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 344bf62..f6d9134 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -38,7 +38,7 @@
         String packageName = appEntry.info.packageName;
         boolean hasPreferred = hasPreferredActivities(pm, packageName)
                 || hasUsbDefaults(usbManager, packageName);
-        int status = pm.getIntentVerificationStatus(packageName, UserHandle.myUserId());
+        int status = pm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId());
         // consider a visible current link-handling state to be any explicitly designated behavior
         boolean hasDomainURLsPreference =
                 status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index e6fe447..d353f31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -316,7 +316,7 @@
         synchronized (mEntriesMap) {
             AppEntry entry = mEntriesMap.get(userId).get(packageName);
             if (entry != null) {
-                mPm.getPackageSizeInfo(packageName, userId, mBackgroundHandler.mStatsObserver);
+                mPm.getPackageSizeInfoAsUser(packageName, userId, mBackgroundHandler.mStatsObserver);
             }
             if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
         }
@@ -906,7 +906,7 @@
                                     entry.sizeLoadStart = now;
                                     mCurComputingSizePkg = entry.info.packageName;
                                     mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
-                                    mPm.getPackageSizeInfo(mCurComputingSizePkg,
+                                    mPm.getPackageSizeInfoAsUser(mCurComputingSizePkg,
                                             mCurComputingSizeUserId, mStatsObserver);
                                 }
                                 if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
new file mode 100755
index 0000000..77f2e19
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2015 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.bluetooth;
+
+import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+final class A2dpSinkProfile implements LocalBluetoothProfile {
+    private static final String TAG = "A2dpSinkProfile";
+    private static boolean V = true;
+
+    private BluetoothA2dpSink mService;
+    private boolean mIsProfileReady;
+
+    private final LocalBluetoothAdapter mLocalAdapter;
+    private final CachedBluetoothDeviceManager mDeviceManager;
+
+    static final ParcelUuid[] SRC_UUIDS = {
+        BluetoothUuid.AudioSource,
+        BluetoothUuid.AdvAudioDist,
+    };
+
+    static final String NAME = "A2DPSink";
+    private final LocalBluetoothProfileManager mProfileManager;
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 5;
+
+    // These callbacks run on the main thread.
+    private final class A2dpSinkServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (V) Log.d(TAG,"Bluetooth service connected");
+            mService = (BluetoothA2dpSink) proxy;
+            // We just bound to the service, so refresh the UI for any connected A2DP devices.
+            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            while (!deviceList.isEmpty()) {
+                BluetoothDevice nextDevice = deviceList.remove(0);
+                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                // we may add a new device here, but generally this should not happen
+                if (device == null) {
+                    Log.w(TAG, "A2dpSinkProfile found new device: " + nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                }
+                device.onProfileStateChanged(A2dpSinkProfile.this, BluetoothProfile.STATE_CONNECTED);
+                device.refresh();
+            }
+            mIsProfileReady=true;
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            mIsProfileReady=false;
+        }
+    }
+
+    public boolean isProfileReady() {
+        return mIsProfileReady;
+    }
+
+    A2dpSinkProfile(Context context, LocalBluetoothAdapter adapter,
+            CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mLocalAdapter = adapter;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+        mLocalAdapter.getProfileProxy(context, new A2dpSinkServiceListener(),
+                BluetoothProfile.A2DP_SINK);
+    }
+
+    public boolean isConnectable() {
+        return true;
+    }
+
+    public boolean isAutoConnectable() {
+        return true;
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        return mService.getDevicesMatchingConnectionStates(
+              new int[] {BluetoothProfile.STATE_CONNECTED,
+                         BluetoothProfile.STATE_CONNECTING,
+                         BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        if (mService == null) return false;
+        List<BluetoothDevice> srcs = getConnectedDevices();
+        if (srcs != null) {
+            for (BluetoothDevice src : srcs) {
+                if (src.equals(device)) {
+                    // Connect to same device, Ignore it
+                    Log.d(TAG,"Ignoring Connect");
+                    return true;
+                }
+            }
+            for (BluetoothDevice src : srcs) {
+                mService.disconnect(src);
+            }
+        }
+        return mService.connect(device);
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        if (mService == null) return false;
+        // Downgrade priority as user is disconnecting the headset.
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
+    }
+
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return mService.getConnectionState(device);
+    }
+
+    public boolean isPreferred(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+    }
+
+    public int getPreferred(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        return mService.getPriority(device);
+    }
+
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+        if (mService == null) return;
+        if (preferred) {
+            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            }
+        } else {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+        }
+    }
+
+    boolean isA2dpPlaying() {
+        if (mService == null) return false;
+        List<BluetoothDevice> srcs = mService.getConnectedDevices();
+        if (!srcs.isEmpty()) {
+            if (mService.isA2dpPlaying(srcs.get(0))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public String toString() {
+        return NAME;
+    }
+
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    public int getNameResource(BluetoothDevice device) {
+        // we need to have same string in UI for even SINK Media Audio.
+        return R.string.bluetooth_profile_a2dp;
+    }
+
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        int state = getConnectionStatus(device);
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return R.string.bluetooth_a2dp_profile_summary_use_for;
+
+            case BluetoothProfile.STATE_CONNECTED:
+                return R.string.bluetooth_a2dp_profile_summary_connected;
+
+            default:
+                return Utils.getConnectionStateSummary(state);
+        }
+    }
+
+    public int getDrawableResource(BluetoothClass btClass) {
+        return R.drawable.ic_bt_headphones_a2dp;
+    }
+
+    protected void finalize() {
+        if (V) Log.d(TAG, "finalize()");
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP_SINK,
+                                                                       mService);
+                mService = null;
+            }catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up A2DP proxy", t);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index d994841..7ee53a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -851,7 +851,8 @@
 
                 case BluetoothProfile.STATE_DISCONNECTED:
                     if (profile.isProfileReady()) {
-                        if (profile instanceof A2dpProfile) {
+                        if ((profile instanceof A2dpProfile)||
+                            (profile instanceof A2dpSinkProfile)){
                             a2dpNotConnected = true;
                         } else if (profile instanceof HeadsetProfile) {
                             headsetNotConnected = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
old mode 100644
new mode 100755
index f935f31..9c5abf3
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -162,6 +162,10 @@
                 if (a2dp != null && a2dp.isA2dpPlaying()) {
                     return;
                 }
+                A2dpSinkProfile a2dpSink = mProfileManager.getA2dpSinkProfile();
+                if ((a2dpSink != null) && (a2dpSink.isA2dpPlaying())){
+                    return;
+                }
             }
 
             if (mAdapter.startDiscovery()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
old mode 100644
new mode 100755
index 8f5e1f1..b05e34c
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.bluetooth;
 
 import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothA2dpSink;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothMap;
@@ -73,6 +74,7 @@
     private final BluetoothEventManager mEventManager;
 
     private A2dpProfile mA2dpProfile;
+    private A2dpSinkProfile mA2dpSinkProfile;
     private HeadsetProfile mHeadsetProfile;
     private MapProfile mMapProfile;
     private final HidProfile mHidProfile;
@@ -136,10 +138,10 @@
      * @param uuids
      */
     void updateLocalProfiles(ParcelUuid[] uuids) {
-        // A2DP
+        // A2DP SRC
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
             if (mA2dpProfile == null) {
-                if(DEBUG) Log.d(TAG, "Adding local A2DP profile");
+                if(DEBUG) Log.d(TAG, "Adding local A2DP SRC profile");
                 mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
                 addProfile(mA2dpProfile, A2dpProfile.NAME,
                         BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
@@ -148,6 +150,17 @@
             Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
         }
 
+        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
+            if (mA2dpSinkProfile == null) {
+                if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile");
+                mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
+                addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
+                        BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
+            }
+        } else if (mA2dpSinkProfile != null) {
+            Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing.");
+        }
+
         // Headset / Handsfree
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
             BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
@@ -288,6 +301,10 @@
         if (profile != null) {
             return profile.isProfileReady();
         }
+        profile = mA2dpSinkProfile;
+        if (profile != null) {
+            return profile.isProfileReady();
+        }
         return false;
     }
 
@@ -295,6 +312,13 @@
         return mA2dpProfile;
     }
 
+    A2dpSinkProfile getA2dpSinkProfile() {
+        if ((mA2dpSinkProfile != null)&&(mA2dpSinkProfile.isProfileReady()))
+        return mA2dpSinkProfile;
+        else
+            return null;
+    }
+
     public HeadsetProfile getHeadsetProfile() {
         return mHeadsetProfile;
     }
@@ -345,6 +369,12 @@
             removedProfiles.remove(mA2dpProfile);
         }
 
+        if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) &&
+                mA2dpSinkProfile != null) {
+                profiles.add(mA2dpSinkProfile);
+                removedProfiles.remove(mA2dpSinkProfile);
+            }
+
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
             mOppProfile != null) {
             profiles.add(mOppProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 9790e64..3b818c8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -422,7 +422,7 @@
                     ActivityManager.getCurrentUser(), currentProfiles, finished, count);
             for (UserInfo user : users) {
                 for (ApplicationInfo app : volumeApps) {
-                    packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
+                    packageManager.getPackageSizeInfoAsUser(app.packageName, user.id, observer);
                 }
             }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 0f322cf..53be0e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -43,22 +43,22 @@
     /**
      * List of the category's children
      */
-    public List<DashboardTile> tiles = new ArrayList<DashboardTile>();
+    public List<Tile> tiles = new ArrayList<Tile>();
 
 
     public DashboardCategory() {
         // Empty
     }
 
-    public void addTile(DashboardTile tile) {
+    public void addTile(Tile tile) {
         tiles.add(tile);
     }
 
-    public void addTile(int n, DashboardTile tile) {
+    public void addTile(int n, Tile tile) {
         tiles.add(n, tile);
     }
 
-    public void removeTile(DashboardTile tile) {
+    public void removeTile(Tile tile) {
         tiles.remove(tile);
     }
 
@@ -70,7 +70,7 @@
         return tiles.size();
     }
 
-    public DashboardTile getTile(int n) {
+    public Tile getTile(int n) {
         return tiles.get(n);
     }
 
@@ -89,7 +89,7 @@
         dest.writeInt(count);
 
         for (int n = 0; n < count; n++) {
-            DashboardTile tile = tiles.get(n);
+            Tile tile = tiles.get(n);
             tile.writeToParcel(dest, flags);
         }
     }
@@ -102,7 +102,7 @@
         final int count = in.readInt();
 
         for (int n = 0; n < count; n++) {
-            DashboardTile tile = DashboardTile.CREATOR.createFromParcel(in);
+            Tile tile = Tile.CREATOR.createFromParcel(in);
             tiles.add(tile);
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
index 793e877d..2fd043f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/ProfileSelectDialog.java
@@ -30,9 +30,9 @@
 
     private static final String ARG_SELECTED_TILE = "selectedTile";
 
-    private DashboardTile mSelectedTile;
+    private Tile mSelectedTile;
 
-    public static void show(FragmentManager manager, DashboardTile tile) {
+    public static void show(FragmentManager manager, Tile tile) {
         ProfileSelectDialog dialog = new ProfileSelectDialog();
         Bundle args = new Bundle();
         args.putParcelable(ARG_SELECTED_TILE, tile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 102e47a..3fc0c22 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -49,7 +49,7 @@
     private static final String TAG = "SettingsDrawerActivity";
 
     private static List<DashboardCategory> sDashboardCategories;
-    private static HashMap<Pair<String, String>, DashboardTile> sTileCache;
+    private static HashMap<Pair<String, String>, Tile> sTileCache;
 
     private final PackageReceiver mPackageReceiver = new PackageReceiver();
     private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
@@ -194,7 +194,7 @@
         }
     }
 
-    public boolean openTile(DashboardTile tile) {
+    public boolean openTile(Tile tile) {
         closeDrawer();
         if (tile == null) {
             return false;
@@ -211,7 +211,7 @@
         return true;
     }
 
-    protected void onTileClicked(DashboardTile tile) {
+    protected void onTileClicked(Tile tile) {
         if (openTile(tile)) {
             finish();
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index 24c6ae8..72f1c566 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -48,7 +48,7 @@
             mItems.add(category);
             for (int j = 0; j < dashboardCategory.tiles.size(); j++) {
                 Item tile = new Item();
-                DashboardTile dashboardTile = dashboardCategory.tiles.get(j);
+                Tile dashboardTile = dashboardCategory.tiles.get(j);
                 tile.label = dashboardTile.title;
                 tile.icon = dashboardTile.icon;
                 tile.tile = dashboardTile;
@@ -58,7 +58,7 @@
         notifyDataSetChanged();
     }
 
-    public DashboardTile getTile(int position) {
+    public Tile getTile(int position) {
         return mItems.get(position).tile;
     }
 
@@ -101,6 +101,6 @@
     private static class Item {
         public Icon icon;
         public CharSequence label;
-        public DashboardTile tile;
+        public Tile tile;
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
similarity index 90%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
rename to packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index 347e9f7..b16cd08 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -29,7 +29,7 @@
 /**
  * Description of a single dashboard tile that the user can select.
  */
-public class DashboardTile implements Parcelable {
+public class Tile implements Parcelable {
 
     /**
      * Title of the tile that is shown to the user.
@@ -79,7 +79,7 @@
      */
     public Bundle metaData;
 
-    public DashboardTile() {
+    public Tile() {
         // Empty
     }
 
@@ -134,16 +134,16 @@
         metaData = in.readBundle();
     }
 
-    DashboardTile(Parcel in) {
+    Tile(Parcel in) {
         readFromParcel(in);
     }
 
-    public static final Creator<DashboardTile> CREATOR = new Creator<DashboardTile>() {
-        public DashboardTile createFromParcel(Parcel source) {
-            return new DashboardTile(source);
+    public static final Creator<Tile> CREATOR = new Creator<Tile>() {
+        public Tile createFromParcel(Parcel source) {
+            return new Tile(source);
         }
-        public DashboardTile[] newArray(int size) {
-            return new DashboardTile[size];
+        public Tile[] newArray(int size) {
+            return new Tile[size];
         }
     };
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 6b36680..2dfdfda 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -1,13 +1,18 @@
 /*
  * Copyright (C) 2015 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
+ * 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.
+ * 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.drawer;
 
 import android.app.ActivityManager;
@@ -108,9 +113,9 @@
     private static final String SETTING_PKG = "com.android.settings";
 
     public static List<DashboardCategory> getCategories(Context context,
-            HashMap<Pair<String, String>, DashboardTile> cache) {
+            HashMap<Pair<String, String>, Tile> cache) {
         final long startTime = System.currentTimeMillis();
-        ArrayList<DashboardTile> tiles = new ArrayList<>();
+        ArrayList<Tile> tiles = new ArrayList<>();
         UserManager userManager = UserManager.get(context);
         for (UserHandle user : userManager.getUserProfiles()) {
             // TODO: Needs much optimization, too many PM queries going on here.
@@ -125,7 +130,7 @@
             getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
         }
         HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
-        for (DashboardTile tile : tiles) {
+        for (Tile tile : tiles) {
             DashboardCategory category = categoryMap.get(tile.category);
             if (category == null) {
                 category = createCategory(context, tile.category);
@@ -170,30 +175,34 @@
     }
 
     private static void getTilesForAction(Context context,
-            UserHandle user, String action, Map<Pair<String, String>, DashboardTile> addedCache,
-            String defaultCategory, ArrayList<DashboardTile> outTiles, boolean requireSettings) {
-        PackageManager pm = context.getPackageManager();
+            UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
+            String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings) {
         Intent intent = new Intent(action);
+        if (requireSettings) {
+            intent.setPackage(SETTING_PKG);
+        }
+        getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
+                requireSettings, true);
+    }
+
+    public static void getTilesForIntent(Context context, UserHandle user, Intent intent,
+            Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
+            boolean usePriority, boolean checkCategory) {
+        PackageManager pm = context.getPackageManager();
         List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
                 PackageManager.GET_META_DATA, user.getIdentifier());
         for (ResolveInfo resolved : results) {
-            if (requireSettings) {
-                if (!SETTING_PKG.equals(resolved.activityInfo.applicationInfo.packageName)) {
-                    continue;
-                }
-            } else {
-                if (!resolved.system) {
-                    // Do not allow any app to add to settings, only system ones.
-                    continue;
-                }
+            if (!resolved.system) {
+                // Do not allow any app to add to settings, only system ones.
+                continue;
             }
             ActivityInfo activityInfo = resolved.activityInfo;
             Bundle metaData = activityInfo.metaData;
             String categoryKey = defaultCategory;
-            if (((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
+            if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
                     && categoryKey == null) {
-                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
-                        + action + " missing metadata "
+                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
+                        + intent + " missing metadata "
                         + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
                 continue;
             } else {
@@ -201,13 +210,13 @@
             }
             Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
                     activityInfo.name);
-            DashboardTile tile = addedCache.get(key);
+            Tile tile = addedCache.get(key);
             if (tile == null) {
-                tile = new DashboardTile();
+                tile = new Tile();
                 tile.intent = new Intent().setClassName(
                         activityInfo.packageName, activityInfo.name);
                 tile.category = categoryKey;
-                tile.priority = requireSettings ? resolved.priority : 0;
+                tile.priority = usePriority ? resolved.priority : 0;
                 tile.metaData = activityInfo.metaData;
                 updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
                         pm);
@@ -234,7 +243,7 @@
         return null;
     }
 
-    private static boolean updateTileData(Context context, DashboardTile tile,
+    private static boolean updateTileData(Context context, Tile tile,
             ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {
         if (applicationInfo.isSystemApp()) {
             int icon = 0;
@@ -252,10 +261,18 @@
                         icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
                     }
                     if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
-                        title = metaData.getString(META_DATA_PREFERENCE_TITLE);
+                        if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
+                            title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
+                        } else {
+                            title = metaData.getString(META_DATA_PREFERENCE_TITLE);
+                        }
                     }
                     if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
-                        summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
+                        if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
+                            summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
+                        } else {
+                            summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
+                        }
                     }
                 }
             } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
@@ -285,10 +302,10 @@
         return false;
     }
 
-    private static final Comparator<DashboardTile> TILE_COMPARATOR =
-            new Comparator<DashboardTile>() {
+    private static final Comparator<Tile> TILE_COMPARATOR =
+            new Comparator<Tile>() {
         @Override
-        public int compare(DashboardTile lhs, DashboardTile rhs) {
+        public int compare(Tile lhs, Tile rhs) {
             return rhs.priority - lhs.priority;
         }
     };
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
new file mode 100644
index 0000000..f1beb10
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -0,0 +1,371 @@
+/*
+ * 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.users;
+
+import android.app.AppGlobals;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class AppRestrictionsHelper {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "AppRestrictionsHelper";
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private final IPackageManager mIPm;
+    private final UserManager mUserManager;
+    private final UserHandle mUser;
+    private final boolean mRestrictedProfile;
+
+    HashMap<String,Boolean> mSelectedPackages = new HashMap<>();
+    private List<SelectableAppInfo> mVisibleApps;
+
+    public AppRestrictionsHelper(Context context, UserHandle user) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mIPm = AppGlobals.getPackageManager();
+        mUser = user;
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted();
+    }
+
+    public void setPackageSelected(String packageName, boolean selected) {
+        mSelectedPackages.put(packageName, selected);
+    }
+
+    public boolean isPackageSelected(String packageName) {
+        return mSelectedPackages.get(packageName);
+    }
+
+    public List<SelectableAppInfo> getVisibleApps() {
+        return mVisibleApps;
+    }
+
+    public void applyUserAppsStates(OnDisableUiForPackageListener listener) {
+        final int userId = mUser.getIdentifier();
+        if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) {
+            Log.e(TAG, "Cannot apply application restrictions on another user!");
+            return;
+        }
+        for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) {
+            String packageName = entry.getKey();
+            boolean enabled = entry.getValue();
+            applyUserAppState(packageName, enabled, listener);
+        }
+    }
+
+    public void applyUserAppState(String packageName, boolean enabled,
+            OnDisableUiForPackageListener listener) {
+        final int userId = mUser.getIdentifier();
+        if (enabled) {
+            // Enable selected apps
+            try {
+                ApplicationInfo info = mIPm.getApplicationInfo(packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+                if (info == null || !info.enabled
+                        || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+                    mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier());
+                    if (DEBUG) {
+                        Log.d(TAG, "Installing " + packageName);
+                    }
+                }
+                if (info != null && (info.privateFlags&ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0
+                        && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) {
+                    listener.onDisableUiForPackage(packageName);
+                    mIPm.setApplicationHiddenSettingAsUser(packageName, false, userId);
+                    if (DEBUG) {
+                        Log.d(TAG, "Unhiding " + packageName);
+                    }
+                }
+            } catch (RemoteException re) {
+                // Ignore
+            }
+        } else {
+            // Blacklist all other apps, system or downloaded
+            try {
+                ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
+                if (info != null) {
+                    if (mRestrictedProfile) {
+                        mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(),
+                                PackageManager.DELETE_SYSTEM_APP);
+                        if (DEBUG) {
+                            Log.d(TAG, "Uninstalling " + packageName);
+                        }
+                    } else {
+                        listener.onDisableUiForPackage(packageName);
+                        mIPm.setApplicationHiddenSettingAsUser(packageName, true, userId);
+                        if (DEBUG) {
+                            Log.d(TAG, "Hiding " + packageName);
+                        }
+                    }
+                }
+            } catch (RemoteException re) {
+                // Ignore
+            }
+        }
+    }
+
+    public void fetchAndMergeApps() {
+        mVisibleApps = new ArrayList<>();
+        final PackageManager pm = mPackageManager;
+        final IPackageManager ipm = mIPm;
+
+        final HashSet<String> excludePackages = new HashSet<>();
+        addSystemImes(excludePackages);
+
+        // Add launchers
+        Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+        launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+        addSystemApps(mVisibleApps, launcherIntent, excludePackages);
+
+        // Add widgets
+        Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        addSystemApps(mVisibleApps, widgetIntent, excludePackages);
+
+        List<ApplicationInfo> installedApps = pm.getInstalledApplications(
+                PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (ApplicationInfo app : installedApps) {
+            // If it's not installed, skip
+            if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+            if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+                // Downloaded app
+                SelectableAppInfo info = new SelectableAppInfo();
+                info.packageName = app.packageName;
+                info.appName = app.loadLabel(pm);
+                info.activityName = info.appName;
+                info.icon = app.loadIcon(pm);
+                mVisibleApps.add(info);
+            } else {
+                try {
+                    PackageInfo pi = pm.getPackageInfo(app.packageName, 0);
+                    // If it's a system app that requires an account and doesn't see restricted
+                    // accounts, mark for removal. It might get shown in the UI if it has an icon
+                    // but will still be marked as false and immutable.
+                    if (mRestrictedProfile
+                            && pi.requiredAccountType != null && pi.restrictedAccountType == null) {
+                        mSelectedPackages.put(app.packageName, false);
+                    }
+                } catch (PackageManager.NameNotFoundException re) {
+                    // Skip
+                }
+            }
+        }
+
+        // Get the list of apps already installed for the user
+        List<ApplicationInfo> userApps = null;
+        try {
+            ParceledListSlice<ApplicationInfo> listSlice = ipm.getInstalledApplications(
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES, mUser.getIdentifier());
+            if (listSlice != null) {
+                userApps = listSlice.getList();
+            }
+        } catch (RemoteException re) {
+            // Ignore
+        }
+
+        if (userApps != null) {
+            for (ApplicationInfo app : userApps) {
+                if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+                if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                        && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+                    // Downloaded app
+                    SelectableAppInfo info = new SelectableAppInfo();
+                    info.packageName = app.packageName;
+                    info.appName = app.loadLabel(pm);
+                    info.activityName = info.appName;
+                    info.icon = app.loadIcon(pm);
+                    mVisibleApps.add(info);
+                }
+            }
+        }
+
+        // Sort the list of visible apps
+        Collections.sort(mVisibleApps, new AppLabelComparator());
+
+        // Remove dupes
+        Set<String> dedupPackageSet = new HashSet<String>();
+        for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
+            SelectableAppInfo info = mVisibleApps.get(i);
+            if (DEBUG) Log.i(TAG, info.toString());
+            String both = info.packageName + "+" + info.activityName;
+            if (!TextUtils.isEmpty(info.packageName)
+                    && !TextUtils.isEmpty(info.activityName)
+                    && dedupPackageSet.contains(both)) {
+                mVisibleApps.remove(i);
+            } else {
+                dedupPackageSet.add(both);
+            }
+        }
+
+        // Establish master/slave relationship for entries that share a package name
+        HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
+        for (SelectableAppInfo info : mVisibleApps) {
+            if (packageMap.containsKey(info.packageName)) {
+                info.masterEntry = packageMap.get(info.packageName);
+            } else {
+                packageMap.put(info.packageName, info);
+            }
+        }
+    }
+
+    /**
+     * Find all pre-installed input methods that are marked as default
+     * and add them to an exclusion list so that they aren't
+     * presented to the user for toggling.
+     * Don't add non-default ones, as they may include other stuff that we
+     * don't need to auto-include.
+     * @param excludePackages the set of package names to append to
+     */
+    private void addSystemImes(Set<String> excludePackages) {
+        InputMethodManager imm = (InputMethodManager)
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+        List<InputMethodInfo> imis = imm.getInputMethodList();
+        for (InputMethodInfo imi : imis) {
+            try {
+                if (imi.isDefault(mContext) && isSystemPackage(imi.getPackageName())) {
+                    excludePackages.add(imi.getPackageName());
+                }
+            } catch (Resources.NotFoundException rnfe) {
+                // Not default
+            }
+        }
+    }
+
+    /**
+     * Add system apps that match an intent to the list, excluding any packages in the exclude list.
+     * @param visibleApps list of apps to append the new list to
+     * @param intent the intent to match
+     * @param excludePackages the set of package names to be excluded, since they're required
+     */
+    private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent,
+            Set<String> excludePackages) {
+        final PackageManager pm = mPackageManager;
+        List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent,
+                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (ResolveInfo app : launchableApps) {
+            if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
+                final String packageName = app.activityInfo.packageName;
+                int flags = app.activityInfo.applicationInfo.flags;
+                if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                        || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                    // System app
+                    // Skip excluded packages
+                    if (excludePackages.contains(packageName)) continue;
+                    int enabled = pm.getApplicationEnabledSetting(packageName);
+                    if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+                        // Check if the app is already enabled for the target user
+                        ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName,
+                                0, mUser);
+                        if (targetUserAppInfo == null
+                                || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+                            continue;
+                        }
+                    }
+                    SelectableAppInfo info = new SelectableAppInfo();
+                    info.packageName = app.activityInfo.packageName;
+                    info.appName = app.activityInfo.applicationInfo.loadLabel(pm);
+                    info.icon = app.activityInfo.loadIcon(pm);
+                    info.activityName = app.activityInfo.loadLabel(pm);
+                    if (info.activityName == null) info.activityName = info.appName;
+
+                    visibleApps.add(info);
+                }
+            }
+        }
+    }
+
+    private boolean isSystemPackage(String packageName) {
+        try {
+            final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
+            if (pi.applicationInfo == null) return false;
+            final int flags = pi.applicationInfo.flags;
+            if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                    || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            // Missing package?
+        }
+        return false;
+    }
+
+    private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) {
+        try {
+            return mIPm.getApplicationInfo(packageName, flags, user.getIdentifier());
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
+    public interface OnDisableUiForPackageListener {
+        void onDisableUiForPackage(String packageName);
+    }
+
+    public static class SelectableAppInfo {
+        public String packageName;
+        public CharSequence appName;
+        public CharSequence activityName;
+        public Drawable icon;
+        public SelectableAppInfo masterEntry;
+
+        @Override
+        public String toString() {
+            return packageName + ": appName=" + appName + "; activityName=" + activityName
+                    + "; icon=" + icon + "; masterEntry=" + masterEntry;
+        }
+    }
+
+    private static class AppLabelComparator implements Comparator<SelectableAppInfo> {
+
+        @Override
+        public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
+            String lhsLabel = lhs.activityName.toString();
+            String rhsLabel = rhs.activityName.toString();
+            return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index cff8c23..2ee4b12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -234,10 +234,13 @@
     }
 
     public boolean matches(WifiConfiguration config) {
-        if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint())
+        if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
             return config.FQDN.equals(mConfig.providerFriendlyName);
-        else
-            return ssid.equals(removeDoubleQuotes(config.SSID)) && security == getSecurity(config);
+        } else {
+            return ssid.equals(removeDoubleQuotes(config.SSID))
+                    && security == getSecurity(config)
+                    && (mConfig == null || mConfig.shared == config.shared);
+        }
     }
 
     public WifiConfiguration getConfig() {
@@ -394,33 +397,20 @@
             summary.append(String.format(format, mConfig.providerFriendlyName));
         } else if (mConfig != null && mConfig.hasNoInternetAccess()) {
             summary.append(mContext.getString(R.string.wifi_no_internet));
-        } else if (mConfig != null && ((mConfig.status == WifiConfiguration.Status.DISABLED &&
-                mConfig.disableReason != WifiConfiguration.DISABLED_UNKNOWN_REASON)
-               || mConfig.autoJoinStatus
-                >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
-            if (mConfig.autoJoinStatus
-                    >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
-                if (mConfig.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE) {
-                    summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
-                } else if (mConfig.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) {
+        } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+            WifiConfiguration.NetworkSelectionStatus networkStatus =
+                    mConfig.getNetworkSelectionStatus();
+            switch (networkStatus.getNetworkSelectionDisableReason()) {
+                case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
                     summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
-                } else {
-                    summary.append(mContext.getString(R.string.wifi_disabled_wifi_failure));
-                }
-            } else {
-                switch (mConfig.disableReason) {
-                    case WifiConfiguration.DISABLED_AUTH_FAILURE:
-                        summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
-                        break;
-                    case WifiConfiguration.DISABLED_DHCP_FAILURE:
-                    case WifiConfiguration.DISABLED_DNS_FAILURE:
-                        summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
-                        break;
-                    case WifiConfiguration.DISABLED_UNKNOWN_REASON:
-                    case WifiConfiguration.DISABLED_ASSOCIATION_REJECT:
-                        summary.append(mContext.getString(R.string.wifi_disabled_generic));
-                        break;
-                }
+                    break;
+                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
+                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
+                    summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+                    break;
+                case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
+                    summary.append(mContext.getString(R.string.wifi_disabled_generic));
+                    break;
             }
         } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
             summary.append(mContext.getString(R.string.wifi_not_in_range));
@@ -437,11 +427,11 @@
                 summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
             }
             summary.append(" " + getVisibilityStatus());
-            if (mConfig != null && mConfig.autoJoinStatus > 0) {
-                summary.append(" (" + mConfig.autoJoinStatus);
-                if (mConfig.blackListTimestamp > 0) {
+            if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+                summary.append(" (" + mConfig.getNetworkSelectionStatus().getNetworkStatusString());
+                if (mConfig.getNetworkSelectionStatus().getDisableTime() > 0) {
                     long now = System.currentTimeMillis();
-                    long diff = (now - mConfig.blackListTimestamp)/1000;
+                    long diff = (now - mConfig.getNetworkSelectionStatus().getDisableTime()) / 1000;
                     long sec = diff%60; //seconds
                     long min = (diff/60)%60; //minutes
                     long hour = (min/60)%60; //hours
@@ -452,17 +442,19 @@
                 }
                 summary.append(")");
             }
-            if (mConfig != null && mConfig.numIpConfigFailures > 0) {
-                summary.append(" ipf=").append(mConfig.numIpConfigFailures);
-            }
-            if (mConfig != null && mConfig.numConnectionFailures > 0) {
-                summary.append(" cf=").append(mConfig.numConnectionFailures);
-            }
-            if (mConfig != null && mConfig.numAuthFailures > 0) {
-                summary.append(" authf=").append(mConfig.numAuthFailures);
-            }
-            if (mConfig != null && mConfig.numNoInternetAccessReports > 0) {
-                summary.append(" noInt=").append(mConfig.numNoInternetAccessReports);
+
+            if (mConfig != null) {
+                WifiConfiguration.NetworkSelectionStatus networkStatus =
+                        mConfig.getNetworkSelectionStatus();
+                for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+                        index < WifiConfiguration.NetworkSelectionStatus
+                        .NETWORK_SELECTION_DISABLED_MAX; index++) {
+                    if (networkStatus.getDisableReasonCounter(index) != 0) {
+                        summary.append(" " + WifiConfiguration.NetworkSelectionStatus
+                                .getNetworkDisableReasonString(index) + "="
+                                + networkStatus.getDisableReasonCounter(index));
+                    }
+                }
             }
         }
         return summary.toString();
@@ -508,10 +500,6 @@
         Map<String, ScanResult> list = mScanResultCache.snapshot();
         // TODO: sort list by RSSI or age
         for (ScanResult result : list.values()) {
-            if (result.seen == 0)
-                continue;
-
-            if (result.autoJoinStatus != ScanResult.ENABLED) numBlackListed++;
 
             if (result.frequency >= LOWER_FREQ_5GHZ
                     && result.frequency <= HIGHER_FREQ_5GHZ) {
@@ -525,8 +513,6 @@
                 num24 = num24 + 1;
             }
 
-            // Ignore results seen, older than 20 seconds
-            if (now - result.seen > VISIBILITY_OUTDATED_AGE_IN_MILLI) continue;
 
             if (result.frequency >= LOWER_FREQ_5GHZ
                     && result.frequency <= HIGHER_FREQ_5GHZ) {
@@ -539,12 +525,6 @@
                     if (bssid != null && result.BSSID.equals(bssid)) scans5GHz.append("*");
                     scans5GHz.append("=").append(result.frequency);
                     scans5GHz.append(",").append(result.level);
-                    if (result.autoJoinStatus != 0) {
-                        scans5GHz.append(",st=").append(result.autoJoinStatus);
-                    }
-                    if (result.numIpConfigFailures != 0) {
-                        scans5GHz.append(",ipf=").append(result.numIpConfigFailures);
-                    }
                     scans5GHz.append("}");
                     n5++;
                 }
@@ -559,12 +539,6 @@
                     if (bssid != null && result.BSSID.equals(bssid)) scans24GHz.append("*");
                     scans24GHz.append("=").append(result.frequency);
                     scans24GHz.append(",").append(result.level);
-                    if (result.autoJoinStatus != 0) {
-                        scans24GHz.append(",st=").append(result.autoJoinStatus);
-                    }
-                    if (result.numIpConfigFailures != 0) {
-                        scans24GHz.append(",ipf=").append(result.numIpConfigFailures);
-                    }
                     scans24GHz.append("}");
                     n24++;
                 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index e12e3a5..e5aeb3c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -229,18 +229,25 @@
     }
 
     private boolean isAlreadyConfiguredCriticalAccessibilitySetting(String name) {
-        // These are the critical accessibility settings that are required for a
-        // blind user to be able to interact with the device. If these settings are
+        // These are the critical accessibility settings that are required for users with
+        // accessibility needs to be able to interact with the device. If these settings are
         // already configured, we will not overwrite them. If they are already set,
-        // it means that the user has performed a global gesture to enable accessibility
-        // and definitely needs these features working after the restore.
+        // it means that the user has performed a global gesture to enable accessibility or set
+        // these settings in the Accessibility portion of the Setup Wizard, and definitely needs
+        // these features working after the restore.
         if (Settings.Secure.ACCESSIBILITY_ENABLED.equals(name)
                 || Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION.equals(name)
                 || Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD.equals(name)
-                || Settings.Secure.TOUCH_EXPLORATION_ENABLED.equals(name)) {
+                || Settings.Secure.TOUCH_EXPLORATION_ENABLED.equals(name)
+                || Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED.equals(name)
+                || Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED.equals(name)
+                || Settings.Secure.UI_NIGHT_MODE.equals(name)) {
             return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
         } else if (Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES.equals(name)
-                || Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(name)) {
+                || Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(name)
+                || Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX.equals(name)
+                || Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER.equals(name)
+                || Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE.equals(name)) {
             return !TextUtils.isEmpty(Settings.Secure.getString(
                     mContext.getContentResolver(), name));
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index bcb459a..57d495f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1010,6 +1010,11 @@
                 break;
 
             default:
+                if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) {
+                    if ("0".equals(value)) return false;
+                    restriction = UserManager.DISALLOW_DATA_ROAMING;
+                    break;
+                }
                 return false;
         }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
index f9f1d3f..2317dbc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
@@ -48,6 +48,23 @@
      */
     private static int STATE_VERSION = 1;
 
+    /**
+     * write the Network selecton status to Byte Array
+     */
+    private static void writeNetworkSelectionStatus(WifiConfiguration config, DataOutputStream dest)
+            throws IOException {
+        WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+
+        dest.writeInt(status.getNetworkSelectionStatus());
+        dest.writeInt(status.getNetworkSelectionDisableReason());
+        for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+                index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX;
+                index++) {
+            dest.writeInt(status.getDisableReasonCounter(index));
+        }
+        dest.writeLong(status.getDisableTime());
+        writeString(dest, status.getNetworkSelectionBSSID());
+    }
 
     /**
      * Marshals a WifiConfig object into a byte-array.
@@ -64,12 +81,11 @@
                 out.writeInt(STATE_VERSION);
                 out.writeInt(wifiConfig.networkId);
                 out.writeInt(wifiConfig.status);
-                out.writeInt(wifiConfig.disableReason);
+                writeNetworkSelectionStatus(wifiConfig, out);
                 writeString(out, wifiConfig.SSID);
                 writeString(out, wifiConfig.BSSID);
                 out.writeInt(wifiConfig.apBand);
                 out.writeInt(wifiConfig.apChannel);
-                writeString(out, wifiConfig.autoJoinBSSID);
                 writeString(out, wifiConfig.FQDN);
                 writeString(out, wifiConfig.providerFriendlyName);
                 out.writeInt(wifiConfig.roamingConsortiumIds.length);
@@ -98,7 +114,6 @@
 
                 writeString(out, wifiConfig.dhcpServer);
                 writeString(out, wifiConfig.defaultGwMacAddress);
-                out.writeInt(wifiConfig.autoJoinStatus);
                 out.writeInt(wifiConfig.selfAdded ? 1 : 0);
                 out.writeInt(wifiConfig.didSelfAdd ? 1 : 0);
                 out.writeInt(wifiConfig.validatedInternetAccess ? 1 : 0);
@@ -108,14 +123,9 @@
                 out.writeInt(wifiConfig.lastUpdateUid);
                 writeString(out, wifiConfig.creatorName);
                 writeString(out, wifiConfig.lastUpdateName);
-                out.writeLong(wifiConfig.blackListTimestamp);
                 out.writeLong(wifiConfig.lastConnectionFailure);
                 out.writeLong(wifiConfig.lastRoamingFailure);
                 out.writeInt(wifiConfig.lastRoamingFailureReason);
-                out.writeLong(wifiConfig.roamingFailureBlackListTimeMilli);
-                out.writeLong(wifiConfig.numConnectionFailures);
-                out.writeLong(wifiConfig.numIpConfigFailures);
-                out.writeInt(wifiConfig.numAuthFailures);
                 out.writeInt(wifiConfig.numScorerOverride);
                 out.writeInt(wifiConfig.numScorerOverrideAndSwitchedNetwork);
                 out.writeInt(wifiConfig.numAssociation);
@@ -126,8 +136,6 @@
                 out.writeInt(wifiConfig.numTicksAtBadRSSI);
                 out.writeInt(wifiConfig.numTicksAtNotHighRSSI);
                 out.writeInt(wifiConfig.numUserTriggeredJoinAttempts);
-                out.writeInt(wifiConfig.autoJoinUseAggressiveJoinAttemptThreshold);
-                out.writeInt(wifiConfig.autoJoinBailedDueToLowRssi ? 1 : 0);
                 out.writeInt(wifiConfig.userApproved);
                 out.writeInt(wifiConfig.numNoInternetAccessReports);
                 out.writeInt(wifiConfig.noInternetAccessExpected ? 1 : 0);
@@ -140,6 +148,23 @@
     }
 
     /**
+     *
+     */
+    private static void readNetworkSelectionStatusFromByteArray(DataInputStream in,
+            WifiConfiguration config, int version) throws IOException {
+        WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+        status.setNetworkSelectionStatus(in.readInt());
+        status.setNetworkSelectionDisableReason(in.readInt());
+        for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+                index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX;
+                index++) {
+            status.setDisableReasonCounter(index, in.readInt());
+        }
+        status.setDisableTime(in.readLong());
+        status.setNetworkSelectionBSSID(readString(in, version));
+    }
+
+    /**
      * Unmarshals a byte array into a WifiConfig Object
      *
      * @param data - marshalled WifiConfig Object
@@ -157,12 +182,11 @@
 
             config.networkId = in.readInt();
             config.status = in.readInt();
-            config.disableReason = in.readInt();
+            readNetworkSelectionStatusFromByteArray(in, config, version);
             config.SSID = readString(in, version);
             config.BSSID = readString(in, version);
             config.apBand = in.readInt();
             config.apChannel = in.readInt();
-            config.autoJoinBSSID = readString(in, version);
             config.FQDN = readString(in, version);
             config.providerFriendlyName = readString(in, version);
             int numRoamingConsortiumIds = in.readInt();
@@ -195,7 +219,6 @@
 
             config.dhcpServer = readString(in, version);
             config.defaultGwMacAddress = readString(in, version);
-            config.autoJoinStatus = in.readInt();
             config.selfAdded = in.readInt() != 0;
             config.didSelfAdd = in.readInt() != 0;
             config.validatedInternetAccess = in.readInt() != 0;
@@ -205,14 +228,10 @@
             config.lastUpdateUid = in.readInt();
             config.creatorName = readString(in, version);
             config.lastUpdateName = readString(in, version);
-            config.blackListTimestamp = in.readLong();
             config.lastConnectionFailure = in.readLong();
             config.lastRoamingFailure = in.readLong();
             config.lastRoamingFailureReason = in.readInt();
             config.roamingFailureBlackListTimeMilli = in.readLong();
-            config.numConnectionFailures = in.readInt();
-            config.numIpConfigFailures = in.readInt();
-            config.numAuthFailures = in.readInt();
             config.numScorerOverride = in.readInt();
             config.numScorerOverrideAndSwitchedNetwork = in.readInt();
             config.numAssociation = in.readInt();
@@ -223,8 +242,6 @@
             config.numTicksAtBadRSSI = in.readInt();
             config.numTicksAtNotHighRSSI = in.readInt();
             config.numUserTriggeredJoinAttempts = in.readInt();
-            config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
-            config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
             config.userApproved = in.readInt();
             config.numNoInternetAccessReports = in.readInt();
             config.noInternetAccessExpected = in.readInt() != 0;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c6d9e98..7416fb5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -151,6 +151,14 @@
             </intent-filter>
         </receiver>
 
+        <receiver
+            android:name=".RemoteBugreportReceiver"
+            android:permission="android.permission.DUMP">
+            <intent-filter>
+                <action android:name="android.intent.action.REMOTE_BUGREPORT_FINISHED" />
+            </intent-filter>
+        </receiver>
+
         <service
             android:name=".BugreportProgressService"
             android:exported="false"/>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index d31088c..df8fad4 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -21,6 +21,7 @@
 import static com.android.shell.BugreportPrefs.getWarningState;
 
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -28,10 +29,13 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
 import java.text.NumberFormat;
 import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
 import libcore.io.Streams;
@@ -61,6 +65,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemProperties;
 import android.os.Vibrator;
@@ -104,7 +109,7 @@
  * </ol>
  */
 public class BugreportProgressService extends Service {
-    static final String TAG = "Shell";
+    private static final String TAG = "BugreportProgressService";
     private static final boolean DEBUG = false;
 
     private static final String AUTHORITY = "com.android.shell";
@@ -112,6 +117,10 @@
     // External intents sent by dumpstate.
     static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED";
     static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED";
+    static final String INTENT_REMOTE_BUGREPORT_FINISHED =
+            "android.intent.action.REMOTE_BUGREPORT_FINISHED";
+    static final String INTENT_REMOTE_BUGREPORT_DISPATCH =
+            "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
 
     // Internal intents used on notification actions.
     static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
@@ -129,6 +138,7 @@
     static final String EXTRA_TITLE = "android.intent.extra.TITLE";
     static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
     static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT";
+    static final String EXTRA_INFO = "android.intent.extra.INFO";
 
     private static final int MSG_SERVICE_COMMAND = 1;
     private static final int MSG_POLL = 2;
@@ -317,7 +327,7 @@
                     takeScreenshot(pid, true);
                     break;
                 case INTENT_BUGREPORT_SHARE:
-                    shareBugreport(pid);
+                    shareBugreport(pid, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
                     break;
                 case INTENT_BUGREPORT_CANCEL:
                     cancel(pid);
@@ -449,6 +459,11 @@
                 .addAction(cancelAction)
                 .build();
 
+        if (info.finished) {
+            Log.w(TAG, "Not sending progress notification because bugreport has finished already ("
+                    + info + ")");
+            return;
+        }
         NotificationManager.from(mContext).notify(TAG, info.pid, notification);
     }
 
@@ -470,6 +485,7 @@
         if (mProcesses.indexOfKey(pid) < 0) {
             Log.w(TAG, "PID not watched: " + pid);
         } else {
+            Log.d(TAG, "Removing PID " + pid);
             mProcesses.remove(pid);
         }
         stopSelfWhenDone();
@@ -628,7 +644,12 @@
         synchronized (BugreportProgressService.this) {
             mTakingScreenshot = flag;
             for (int i = 0; i < mProcesses.size(); i++) {
-                updateProgress(mProcesses.valueAt(i));
+                final BugreportInfo info = mProcesses.valueAt(i);
+                if (info.finished) {
+                    Log.d(TAG, "Not updating progress because share notification was already sent");
+                    continue;
+                }
+                updateProgress(info);
             }
         }
     }
@@ -657,6 +678,11 @@
         final int msgId;
         if (taken) {
             info.addScreenshot(screenshotFile);
+            if (info.finished) {
+                Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
+                info.renameScreenshots(mScreenshotsDir);
+                sendBugreportNotification(mContext, info);
+            }
             msgId = R.string.bugreport_screenshot_taken;
         } else {
             // TODO: try again using Framework APIs instead of relying on screencap.
@@ -796,13 +822,17 @@
      * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
      * intent, but issuing a warning dialog the first time.
      */
-    private void shareBugreport(int pid) {
-        final BugreportInfo info = getInfo(pid);
+    private void shareBugreport(int pid, BugreportInfo sharedInfo) {
+        BugreportInfo info = getInfo(pid);
         if (info == null) {
-            // Should not happen, so log if it does...
-            Log.e(TAG, "INTERNAL ERROR: no info for PID " + pid + ": " + mProcesses);
-            return;
+            // Service was terminated but notification persisted
+            info = sharedInfo;
+            Log.d(TAG, "shareBugreport(): no info for PID " + pid + " on managed processes ("
+                    + mProcesses + "), using info from intent instead (" + info + ")");
         }
+
+        addDetailsToZipFile(info);
+
         final Intent sendIntent = buildSendIntent(mContext, info);
         final Intent notifIntent;
 
@@ -829,6 +859,7 @@
         shareIntent.setClass(context, BugreportProgressService.class);
         shareIntent.setAction(INTENT_BUGREPORT_SHARE);
         shareIntent.putExtra(EXTRA_PID, info.pid);
+        shareIntent.putExtra(EXTRA_INFO, info);
 
         final String title = context.getString(R.string.bugreport_finished_title);
         final Notification.Builder builder = new Notification.Builder(context)
@@ -858,7 +889,7 @@
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                info.bugreportFile = zipBugreport(info.bugreportFile);
+                zipBugreport(info);
                 sendBugreportNotification(context, info);
                 return null;
             }
@@ -869,35 +900,97 @@
      * Zips a bugreport file, returning the path to the new file (or to the
      * original in case of failure).
      */
-    private static File zipBugreport(File bugreportFile) {
-        String bugreportPath = bugreportFile.getAbsolutePath();
-        String zippedPath = bugreportPath.replace(".txt", ".zip");
+    private static void zipBugreport(BugreportInfo info) {
+        final String bugreportPath = info.bugreportFile.getAbsolutePath();
+        final String zippedPath = bugreportPath.replace(".txt", ".zip");
         Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath);
-        File bugreportZippedFile = new File(zippedPath);
-        try (InputStream is = new FileInputStream(bugreportFile);
+        final File bugreportZippedFile = new File(zippedPath);
+        try (InputStream is = new FileInputStream(info.bugreportFile);
                 ZipOutputStream zos = new ZipOutputStream(
                         new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
-            ZipEntry entry = new ZipEntry(bugreportFile.getName());
-            entry.setTime(bugreportFile.lastModified());
-            zos.putNextEntry(entry);
-            int totalBytes = Streams.copy(is, zos);
-            Log.v(TAG, "size of original bugreport: " + totalBytes + " bytes");
-            zos.closeEntry();
-            // Delete old file;
-            boolean deleted = bugreportFile.delete();
+            addEntry(zos, info.bugreportFile.getName(), is);
+            // Delete old file
+            final boolean deleted = info.bugreportFile.delete();
             if (deleted) {
                 Log.v(TAG, "deleted original bugreport (" + bugreportPath + ")");
             } else {
                 Log.e(TAG, "could not delete original bugreport (" + bugreportPath + ")");
             }
-            return bugreportZippedFile;
+            info.bugreportFile = bugreportZippedFile;
         } catch (IOException e) {
             Log.e(TAG, "exception zipping file " + zippedPath, e);
-            return bugreportFile; // Return original.
         }
     }
 
     /**
+     * Adds the user-provided info into the bugreport zip file.
+     * <p>
+     * If user provided a title, it will be saved into a {@code title.txt} entry; similarly, the
+     * description will be saved on {@code description.txt}.
+     */
+    private void addDetailsToZipFile(BugreportInfo info) {
+        if (info.bugreportFile == null) {
+            // One possible reason is a bug in the Parcelization code.
+            Log.e(TAG, "INTERNAL ERROR: no bugreportFile on " + info);
+            return;
+        }
+        // It's not possible to add a new entry into an existing file, so we need to create a new
+        // zip, copy all entries, then rename it.
+        final File dir = info.bugreportFile.getParentFile();
+        final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
+        Log.d(TAG, "Writing temporary zip file (" + tmpZip + ")");
+        try (ZipFile oldZip = new ZipFile(info.bugreportFile);
+                ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpZip))) {
+
+            // First copy contents from original zip.
+            Enumeration<? extends ZipEntry> entries = oldZip.entries();
+            while (entries.hasMoreElements()) {
+                final ZipEntry entry = entries.nextElement();
+                final String entryName = entry.getName();
+                if (!entry.isDirectory()) {
+                    addEntry(zos, entryName, entry.getTime(), oldZip.getInputStream(entry));
+                } else {
+                    Log.w(TAG, "skipping directory entry: " + entryName);
+                }
+            }
+
+            // Then add the user-provided info.
+            addEntry(zos, "title.txt", info.title);
+            addEntry(zos, "description.txt", info.description);
+        } catch (IOException e) {
+            Log.e(TAG, "exception zipping file " + tmpZip, e);
+            return;
+        }
+
+        if (!tmpZip.renameTo(info.bugreportFile)) {
+            Log.e(TAG, "Could not rename " + tmpZip + " to " + info.bugreportFile);
+        }
+    }
+
+    private static void addEntry(ZipOutputStream zos, String entry, String text)
+            throws IOException {
+        if (DEBUG) Log.v(TAG, "adding entry '" + entry + "': " + text);
+        if (!TextUtils.isEmpty(text)) {
+            addEntry(zos, entry, new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)));
+        }
+    }
+
+    private static void addEntry(ZipOutputStream zos, String entryName, InputStream is)
+            throws IOException {
+        addEntry(zos, entryName, System.currentTimeMillis(), is);
+    }
+
+    private static void addEntry(ZipOutputStream zos, String entryName, long timestamp,
+            InputStream is) throws IOException {
+        final ZipEntry entry = new ZipEntry(entryName);
+        entry.setTime(timestamp);
+        zos.putNextEntry(entry);
+        final int totalBytes = Streams.copy(is, zos);
+        if (DEBUG) Log.v(TAG, "size of '" + entryName + "' entry: " + totalBytes + " bytes");
+        zos.closeEntry();
+    }
+
+    /**
      * Find the best matching {@link Account} based on build properties.
      */
     private static Account findSendToAccount(Context context) {
@@ -931,7 +1024,7 @@
         return foundAccount;
     }
 
-    private static Uri getUri(Context context, File file) {
+    static Uri getUri(Context context, File file) {
         return file != null ? FileProvider.getUriForFile(context, AUTHORITY, file) : null;
     }
 
@@ -1203,7 +1296,7 @@
     /**
      * Information about a bugreport process while its in progress.
      */
-    private static final class BugreportInfo {
+    private static final class BugreportInfo implements Parcelable {
         private final Context context;
 
         /**
@@ -1247,6 +1340,11 @@
         long lastUpdate = System.currentTimeMillis();
 
         /**
+         * Time of the last progress update when Parcel was created.
+         */
+        String formattedLastUpdate;
+
+        /**
          * Path of the main bugreport file.
          */
         File bugreportFile;
@@ -1325,6 +1423,11 @@
         }
 
         String getFormattedLastUpdate() {
+            if (context == null) {
+                // Restored from Parcel
+                return formattedLastUpdate == null ?
+                        Long.toString(lastUpdate) : formattedLastUpdate;
+            }
             return DateUtils.formatDateTime(context, lastUpdate,
                     DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
         }
@@ -1338,5 +1441,74 @@
                     + "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
                     + "\n\tlast_update: " + getFormattedLastUpdate();
         }
+
+        // Parcelable contract
+        protected BugreportInfo(Parcel in) {
+            context = null;
+            pid = in.readInt();
+            name = in.readString();
+            title = in.readString();
+            description = in.readString();
+            max = in.readInt();
+            progress = in.readInt();
+            lastUpdate = in.readLong();
+            formattedLastUpdate = in.readString();
+            bugreportFile = readFile(in);
+
+            int screenshotSize = in.readInt();
+            for (int i = 1; i <= screenshotSize; i++) {
+                  screenshotFiles.add(readFile(in));
+            }
+
+            finished = in.readInt() == 1;
+            screenshotCounter = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(pid);
+            dest.writeString(name);
+            dest.writeString(title);
+            dest.writeString(description);
+            dest.writeInt(max);
+            dest.writeInt(progress);
+            dest.writeLong(lastUpdate);
+            dest.writeString(getFormattedLastUpdate());
+            writeFile(dest, bugreportFile);
+
+            dest.writeInt(screenshotFiles.size());
+            for (File screenshotFile : screenshotFiles) {
+                writeFile(dest, screenshotFile);
+            }
+
+            dest.writeInt(finished ? 1 : 0);
+            dest.writeInt(screenshotCounter);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        private void writeFile(Parcel dest, File file) {
+            dest.writeString(file == null ? null : file.getPath());
+        }
+
+        private File readFile(Parcel in) {
+            final String path = in.readString();
+            return path == null ? null : new File(path);
+        }
+
+        public static final Parcelable.Creator<BugreportInfo> CREATOR =
+                new Parcelable.Creator<BugreportInfo>() {
+            public BugreportInfo createFromParcel(Parcel source) {
+                return new BugreportInfo(source);
+            }
+
+            public BugreportInfo[] newArray(int size) {
+                return new BugreportInfo[size];
+            }
+        };
+
     }
 }
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index b818343..9afa900 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -19,7 +19,6 @@
 import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
 import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
 import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.TAG;
 import static com.android.shell.BugreportProgressService.getFileExtra;
 
 import java.io.File;
@@ -37,6 +36,7 @@
  * {@link Intent#ACTION_SEND_MULTIPLE}.
  */
 public class BugreportReceiver extends BroadcastReceiver {
+    private static final String TAG = "BugreportReceiver";
 
     /**
      * Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are
@@ -52,7 +52,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         // Clean up older bugreports in background
-        cleanupOldFiles(intent);
+        cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE);
 
         // Delegate intent handling to service.
         Intent serviceIntent = new Intent(context, BugreportProgressService.class);
@@ -60,8 +60,9 @@
         context.startService(serviceIntent);
     }
 
-    private void cleanupOldFiles(Intent intent) {
-        if (!INTENT_BUGREPORT_FINISHED.equals(intent.getAction())) {
+    static void cleanupOldFiles(BroadcastReceiver br, Intent intent, String expectedAction,
+            final int minCount, final long minAge) {
+        if (!expectedAction.equals(intent.getAction())) {
             return;
         }
         final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
@@ -69,12 +70,15 @@
             Log.e(TAG, "Not deleting old files because file " + bugreportFile + " doesn't exist");
             return;
         }
-        final PendingResult result = goAsync();
+        final PendingResult result = br.goAsync();
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                FileUtils.deleteOlderFiles(
-                        bugreportFile.getParentFile(), MIN_KEEP_COUNT, MIN_KEEP_AGE);
+                try {
+                    FileUtils.deleteOlderFiles(bugreportFile.getParentFile(), minCount, minAge);
+                } catch (RuntimeException e) {
+                    Log.e(TAG, "RuntimeException deleting old files", e);
+                }
                 result.finish();
                 return null;
             }
diff --git a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
new file mode 100644
index 0000000..6f783a1
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 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.shell;
+
+import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
+import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_FINISHED;
+import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_DISPATCH;
+import static com.android.shell.BugreportProgressService.getFileExtra;
+import static com.android.shell.BugreportProgressService.getUri;
+import static com.android.shell.BugreportReceiver.cleanupOldFiles;
+
+import java.io.File;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.UserHandle;
+
+/**
+ * Receiver that handles finished remote bugreports, by re-sending
+ * the intent with appended bugreport zip file URI.
+ *
+ * <p> Remote bugreport never contains a screenshot.
+ */
+public class RemoteBugreportReceiver extends BroadcastReceiver {
+
+    private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
+    private static final String EXTRA_REMOTE_BUGREPORT_HASH =
+            "android.intent.extra.REMOTE_BUGREPORT_HASH";
+
+    /** Always keep just the last remote bugreport zip file */
+    private static final int MIN_KEEP_COUNT = 1;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        cleanupOldFiles(this, intent, INTENT_REMOTE_BUGREPORT_FINISHED, MIN_KEEP_COUNT, 0);
+
+        final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
+        final Uri bugreportUri = getUri(context, bugreportFile);
+        final String bugreportHash = intent.getStringExtra(EXTRA_REMOTE_BUGREPORT_HASH);
+
+        final Intent newIntent = new Intent(INTENT_REMOTE_BUGREPORT_DISPATCH);
+        newIntent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
+        newIntent.putExtra(EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
+        context.sendBroadcastAsUser(newIntent, UserHandle.SYSTEM,
+                android.Manifest.permission.DUMP);
+    }
+}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 6bee767..52e1b56 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -60,10 +60,12 @@
 import android.support.test.uiautomator.UiObject;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
 
 import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
+import com.android.shell.BugreportProgressService;
 
 /**
  * Integration tests for {@link BugreportReceiver}.
@@ -88,6 +90,9 @@
     // Timeout for UI operations, in milliseconds.
     private static final int TIMEOUT = (int) BugreportProgressService.POLLING_FREQUENCY * 4;
 
+    // Timeout for when waiting for a screenshot to finish.
+    private static final int SAFE_SCREENSHOT_DELAY = SCREENSHOT_DELAY_SECONDS + 10;
+
     private static final String BUGREPORTS_DIR = "bugreports";
     private static final String BUGREPORT_FILE = "test_bugreport.txt";
     private static final String ZIP_FILE = "test_bugreport.zip";
@@ -103,6 +108,13 @@
     private static final String NAME = "BUG, Y U NO REPORT?";
     private static final String NEW_NAME = "Bug_Forrest_Bug";
     private static final String TITLE = "Wimbugdom Champion 2015";
+
+    private static final String NO_DESCRIPTION = null;
+    private static final String NO_NAME = null;
+    private static final String NO_SCREENSHOT = null;
+    private static final String NO_TITLE = null;
+    private static final Integer NO_PID = null;
+
     private String mDescription;
 
     private String mPlainTextPath;
@@ -115,6 +127,7 @@
 
     @Override
     protected void setUp() throws Exception {
+        Log.i(TAG, "#### setup() on " + getName());
         Instrumentation instrumentation = getInstrumentation();
         mContext = instrumentation.getTargetContext();
         mUiBot = new UiBot(UiDevice.getInstance(instrumentation), TIMEOUT);
@@ -142,6 +155,7 @@
     public void testProgress() throws Exception {
         resetProperties();
         sendBugreportStarted(1000);
+        waitForScreenshotButtonEnabled(true);
 
         assertProgressNotification(NAME, "0.00%");
 
@@ -156,13 +170,21 @@
 
         Bundle extras =
                 sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NAME, ZIP_FILE,
-                null, 1);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+                NAME, NO_TITLE, NO_DESCRIPTION, 1, true);
 
         assertServiceNotRunning();
     }
 
     public void testProgress_takeExtraScreenshot() throws Exception {
+        takeExtraScreenshotTest(false);
+    }
+
+    public void testProgress_takeExtraScreenshotServiceDiesAfterScreenshotTaken() throws Exception {
+        takeExtraScreenshotTest(true);
+    }
+
+    private void takeExtraScreenshotTest(boolean serviceDies) throws Exception {
         resetProperties();
         sendBugreportStarted(1000);
 
@@ -171,17 +193,54 @@
         assertScreenshotButtonEnabled(false);
         waitForScreenshotButtonEnabled(true);
 
-        Bundle extras =
-                sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NAME, ZIP_FILE,
-                null, 2);
+        sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
+
+        if (serviceDies) {
+            waitShareNotification();
+            killService();
+        }
+
+        Bundle extras = acceptBugreportAndGetSharedIntent();
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
+                NAME, NO_TITLE, NO_DESCRIPTION, 2, true);
 
         assertServiceNotRunning();
     }
 
-    public void testProgress_changeDetails() throws Exception {
+    public void testScreenshotFinishesAfterBugreport() throws Exception {
+        screenshotFinishesAfterBugreportTest(false);
+    }
+
+    public void testScreenshotFinishesAfterBugreportAndServiceDiesBeforeSharing() throws Exception {
+        screenshotFinishesAfterBugreportTest(true);
+    }
+
+    private void screenshotFinishesAfterBugreportTest(boolean serviceDies) throws Exception {
+        resetProperties();
+
+        sendBugreportStarted(1000);
+        sendBugreportFinished(PID, mPlainTextPath, NO_SCREENSHOT);
+        waitShareNotification();
+
+        // There's no indication in the UI about the screenshot finish, so just sleep like a baby...
+        Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
+
+        if (serviceDies) {
+            killService();
+        }
+
+        Bundle extras = acceptBugreportAndGetSharedIntent();
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, PID, ZIP_FILE,
+                NAME, NO_TITLE, NO_DESCRIPTION, 1, true);
+
+        assertServiceNotRunning();
+    }
+
+    public void testProgress_changeDetailsInvalidInput() throws Exception {
+
         resetProperties();
         sendBugreportStarted(1000);
+        waitForScreenshotButtonEnabled(true);
 
         DetailsUi detailsUi = new DetailsUi(mUiBot);
 
@@ -217,15 +276,73 @@
 
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath,
                 mScreenshotPath);
-        assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NEW_NAME, TITLE,
-                mDescription, 1);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+                NEW_NAME, TITLE, mDescription, 1, true);
 
         assertServiceNotRunning();
     }
 
-    public void testProgress_bugreportFinishedWhileChangingDetails() throws Exception {
+    public void testProgress_changeDetailsPlainBugreport() throws Exception {
+        changeDetailsTest(true);
+    }
+
+    public void testProgress_changeDetailsZippedBugreport() throws Exception {
+        changeDetailsTest(false);
+    }
+
+    public void changeDetailsTest(boolean plainText) throws Exception {
+
         resetProperties();
         sendBugreportStarted(1000);
+        waitForScreenshotButtonEnabled(true);
+
+        DetailsUi detailsUi = new DetailsUi(mUiBot);
+
+        // Check initial name.
+        String actualName = detailsUi.nameField.getText().toString();
+        assertEquals("Wrong value on field 'name'", NAME, actualName);
+
+        // Change fields.
+        detailsUi.reOpen();
+        detailsUi.nameField.setText(NEW_NAME);
+        detailsUi.titleField.setText(TITLE);
+        detailsUi.descField.setText(mDescription);
+
+        detailsUi.clickOk();
+
+        assertPropertyValue(NAME_PROPERTY, NEW_NAME);
+        assertProgressNotification(NEW_NAME, "0.00%");
+
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID,
+                plainText? mPlainTextPath : mZipPath, mScreenshotPath);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+                NEW_NAME, TITLE, mDescription, 1, true);
+
+        assertServiceNotRunning();
+    }
+
+    /**
+     * Tests the scenario where the initial screenshot and dumpstate are finished while the user
+     * is changing the info in the details screen.
+     */
+    public void testProgress_bugreportAndScreenshotFinishedWhileChangingDetails() throws Exception {
+        bugreportFinishedWhileChangingDetailsTest(false);
+    }
+
+    /**
+     * Tests the scenario where dumpstate is finished while the user is changing the info in the
+     * details screen, but the initial screenshot finishes afterwards.
+     */
+    public void testProgress_bugreportFinishedWhileChangingDetails() throws Exception {
+        bugreportFinishedWhileChangingDetailsTest(true);
+    }
+
+    private void bugreportFinishedWhileChangingDetailsTest(boolean waitScreenshot) throws Exception {
+        resetProperties();
+        sendBugreportStarted(1000);
+        if (waitScreenshot) {
+            waitForScreenshotButtonEnabled(true);
+        }
 
         DetailsUi detailsUi = new DetailsUi(mUiBot);
 
@@ -233,7 +350,7 @@
         detailsUi.nameField.setText(NEW_NAME);
         sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
 
-        // Wait until the share notifcation is received...
+        // Wait until the share notification is received...
         mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
         // ...then close notification bar.
         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
@@ -249,8 +366,8 @@
 
         // Finally, share bugreport.
         Bundle extras = acceptBugreportAndGetSharedIntent();
-        assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, NAME, TITLE,
-                mDescription, 1);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+                NAME, TITLE, mDescription, 1, true);
 
         assertServiceNotRunning();
     }
@@ -260,7 +377,7 @@
         BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
 
         // Send notification and click on share.
-        sendBugreportFinished(null, mPlainTextPath, null);
+        sendBugreportFinished(NO_PID, mPlainTextPath, null);
         acceptBugreport();
 
         // Handle the warning
@@ -275,13 +392,20 @@
         // Share the bugreport.
         mUiBot.chooseActivity(UI_NAME);
         Bundle extras = mListener.getExtras();
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
 
         // Make sure it's hidden now.
         int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
         assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
     }
 
+    public void testShareBugreportAfterServiceDies() throws Exception {
+        sendBugreportFinished(NO_PID, mPlainTextPath, NO_SCREENSHOT);
+        killService();
+        Bundle extras = acceptBugreportAndGetSharedIntent();
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
+    }
+
     public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
         Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, mScreenshotPath);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
@@ -293,13 +417,13 @@
     }
 
     public void testBugreportFinished_plainBugreportAndNoScreenshot() throws Exception {
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, null);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, NO_SCREENSHOT);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
     }
 
     public void testBugreportFinished_zippedBugreportAndNoScreenshot() throws Exception {
-        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, null);
-        assertActionSendMultiple(extras, BUGREPORT_CONTENT, null);
+        Bundle extras = sendBugreportFinishedAndGetSharedIntent(mZipPath, NO_SCREENSHOT);
+        assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
     }
 
     private void cancelExistingNotifications() {
@@ -376,6 +500,13 @@
     }
 
     /**
+     * Waits for the notification to share the finished bugreport.
+     */
+    private void waitShareNotification() {
+        mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
+    }
+
+    /**
      * Accepts the notification to share the finished bugreport.
      */
     private void acceptBugreport() {
@@ -405,8 +536,8 @@
      */
     private void assertActionSendMultiple(Bundle extras, String bugreportContent,
             String screenshotContent) throws IOException {
-        assertActionSendMultiple(extras, bugreportContent, screenshotContent, PID, null, ZIP_FILE,
-                null, 0);
+        assertActionSendMultiple(extras, bugreportContent, screenshotContent, PID, ZIP_FILE,
+                NO_NAME, NO_TITLE, NO_DESCRIPTION, 0, false);
     }
 
     /**
@@ -416,14 +547,17 @@
      * @param bugreportContent expected content in the bugreport file
      * @param screenshotContent expected content in the screenshot file (sent by dumpstate), if any
      * @param pid emulated dumpstate pid
-     * @param name bugreport name as provided by the user
-     * @param title bugreport name as provided by the user (or received by dumpstate)
+     * @param name expected subject
+     * @param name bugreport name as provided by the user (or received by dumpstate)
+     * @param title bugreport name as provided by the user
      * @param description bugreport description as provided by the user
      * @param numberScreenshots expected number of screenshots taken by Shell.
+     * @param renamedScreenshots whether the screenshots are expected to be renamed
      */
     private void assertActionSendMultiple(Bundle extras, String bugreportContent,
-            String screenshotContent, int pid, String name, String title, String description,
-            int numberScreenshots) throws IOException {
+            String screenshotContent, int pid, String subject,
+            String name, String title, String description,
+            int numberScreenshots, boolean renamedScreenshots) throws IOException {
         String body = extras.getString(Intent.EXTRA_TEXT);
         assertContainsRegex("missing build info",
                 SystemProperties.get("ro.build.description"), body);
@@ -433,7 +567,7 @@
             assertContainsRegex("missing description", description, body);
         }
 
-        assertEquals("wrong subject", title, extras.getString(Intent.EXTRA_SUBJECT));
+        assertEquals("wrong subject", subject, extras.getString(Intent.EXTRA_SUBJECT));
 
         List<Uri> attachments = extras.getParcelableArrayList(Intent.EXTRA_STREAM);
         int expectedNumberScreenshots = numberScreenshots;
@@ -441,7 +575,8 @@
             expectedNumberScreenshots ++; // Add screenshot received by dumpstate
         }
         int expectedSize = expectedNumberScreenshots + 1; // All screenshots plus the bugreport file
-        assertEquals("wrong number of attachments", expectedSize, attachments.size());
+        assertEquals("wrong number of attachments (" + attachments + ")",
+                expectedSize, attachments.size());
 
         // Need to interact through all attachments, since order is not guaranteed.
         Uri zipUri = null;
@@ -456,6 +591,12 @@
         }
         assertNotNull("did not get .zip attachment", zipUri);
         assertZipContent(zipUri, BUGREPORT_FILE, BUGREPORT_CONTENT);
+        if (!TextUtils.isEmpty(title)) {
+            assertZipContent(zipUri, "title.txt", title);
+        }
+        if (!TextUtils.isEmpty(description)) {
+            assertZipContent(zipUri, "description.txt", description);
+        }
 
         // URI of the screenshot taken by dumpstate.
         Uri externalScreenshotUri = null;
@@ -480,7 +621,7 @@
         // Check internal screenshots.
         SortedSet<String> expectedNames = new TreeSet<>();
         for (int i = 1 ; i <= numberScreenshots; i++) {
-            String prefix = name != null ? name : Integer.toString(pid);
+            String prefix = renamedScreenshots  ? name : Integer.toString(pid);
             String expectedName = "screenshot-" + prefix + "-" + i + ".png";
             expectedNames.add(expectedName);
         }
@@ -530,6 +671,15 @@
         assertFalse("Service '" + service + "' is still running", isServiceRunning(service));
     }
 
+    private void killService() {
+        waitForService(true);
+        Log.v(TAG, "Stopping service");
+        boolean stopped = mContext.stopService(new Intent(mContext, BugreportProgressService.class));
+        Log.d(TAG, "stopService returned " + stopped);
+        waitForService(false);
+        assertServiceNotRunning();  // Sanity check.
+    }
+
     private boolean isServiceRunning(String name) {
         ActivityManager manager = (ActivityManager) mContext
                 .getSystemService(Context.ACTIVITY_SERVICE);
@@ -541,6 +691,33 @@
         return false;
     }
 
+    private void waitForService(boolean expectRunning) {
+        String service = BugreportProgressService.class.getName();
+        boolean actualRunning;
+        for (int i = 1; i <= 5; i++) {
+            actualRunning = isServiceRunning(service);
+            Log.d(TAG, "Attempt " + i + " to check status of service '"
+                    + service + "': expected=" + expectRunning + ", actual= " + actualRunning);
+            if (actualRunning == expectRunning) {
+                return;
+            }
+            try {
+                Thread.sleep(DateUtils.SECOND_IN_MILLIS);
+            } catch (InterruptedException e) {
+                Log.w(TAG, "thread interrupted");
+                Thread.currentThread().interrupt();
+            }
+        }
+        if (!expectRunning) {
+            // Typically happens when service is waiting for a screenshot to finish.
+            Log.w(TAG, "Service didn't stop; try to kill it again");
+            killService();
+            return;
+        }
+
+        fail("Service status didn't change to " + expectRunning);
+    }
+
     private static void createTextFile(String path, String content) throws IOException {
         Log.v(TAG, "createFile(" + path + ")");
         try (Writer writer = new BufferedWriter(new OutputStreamWriter(
@@ -592,7 +769,7 @@
 
     private UiObject waitForScreenshotButtonEnabled(boolean expectedEnabled) throws Exception {
         UiObject screenshotButton = getScreenshotButton();
-        int maxAttempts = SCREENSHOT_DELAY_SECONDS + 2;
+        int maxAttempts = SAFE_SCREENSHOT_DELAY;
         int i = 0;
         do {
             boolean enabled = screenshotButton.isEnabled();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9546c8d..8d57b88 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -58,6 +58,7 @@
     <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
     <uses-permission android:name="android.permission.CONTROL_VPN" />
     <uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
@@ -142,6 +143,12 @@
     <!-- Block notifications inline notifications -->
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
 
+    <!-- Access battery information -->
+    <uses-permission android:name="android.permission.BATTERY_STATS" />
+
+    <!-- DevicePolicyManager get user restrictions -->
+    <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
@@ -302,6 +309,27 @@
             android:launchMode="singleTop"
             android:excludeFromRecents="true" />
 
+        <!-- started from PipUI -->
+        <activity
+            android:name="com.android.systemui.tv.pip.PipMenuActivity"
+            android:exported="true"
+            android:theme="@style/PipTheme"
+            android:launchMode="singleTop"
+            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|layoutDirection"
+            android:resizeable="true"
+            android:supportsPictureInPicture="true"
+            androidprv:alwaysFocusable="true"
+            android:excludeFromRecents="true" />
+        <activity
+            android:name="com.android.systemui.tv.pip.PipOverlayActivity"
+            android:exported="true"
+            android:theme="@style/PipTheme"
+            android:launchMode="singleTop"
+            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|layoutDirection"
+            android:resizeable="true"
+            android:supportsPictureInPicture="true"
+            android:excludeFromRecents="true" />
+
         <!-- platform logo easter egg activity -->
         <activity
             android:name=".DessertCase"
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6a10c2c..2ea8c9c 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -10,8 +10,10 @@
   public void setGlowScale(float);
 }
 
+-keep class com.android.systemui.statusbar.car.CarStatusBar
 -keep class com.android.systemui.statusbar.phone.PhoneStatusBar
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
+-keep class com.android.systemui.SystemUIFactory
 
 -keepclassmembers class ** {
     public void onBusEvent(**);
diff --git a/packages/SystemUI/res/color/qs_tile_text.xml b/packages/SystemUI/res/color/qs_tile_text.xml
new file mode 100644
index 0000000..90e0bce
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_tile_text.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+    <item android:color="#B3FFFFFF" /> <!-- 70% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_name.xml b/packages/SystemUI/res/color/qs_user_detail_name.xml
index 57f7e65..35c7a4f 100644
--- a/packages/SystemUI/res/color/qs_user_detail_name.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_name.xml
@@ -18,5 +18,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_activated="true" android:color="@color/current_user_border_color" />
+    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
     <item android:color="#66ffffff" /> <!-- 40% white -->
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..6242084
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png
new file mode 100644
index 0000000..1b37a47
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png
new file mode 100644
index 0000000..9e05758
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..2fcfdde
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..48708a5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..3d73184
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..786935d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..e4bd4bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png
new file mode 100644
index 0000000..94ccf79
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png
new file mode 100644
index 0000000..980bbbc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..201be3b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png
new file mode 100644
index 0000000..2770d62
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png
new file mode 100644
index 0000000..8ac6493
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..8e3678b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png
new file mode 100644
index 0000000..6c7cb05
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png
new file mode 100644
index 0000000..ea2b108
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png
new file mode 100644
index 0000000..3e11023
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png
new file mode 100644
index 0000000..fe8213d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_back_ime_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png
new file mode 100644
index 0000000..f3be2ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_docked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png
new file mode 100644
index 0000000..c117efd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_sysbar_home_carmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/SystemUI/res/drawable/car_ic_arrow.xml
new file mode 100644
index 0000000..9d292cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_arrow.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48.0dp"
+        android:height="48.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/>
+    <path
+        android:pathData="M0 0h48v48H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/docked_divider_handle.xml b/packages/SystemUI/res/drawable/docked_divider_handle.xml
deleted file mode 100644
index 84c0343..0000000
--- a/packages/SystemUI/res/drawable/docked_divider_handle.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2 (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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shape="rectangle">
-    <size android:height="@dimen/docked_divider_handle_height"
-        android:width="@dimen/docked_divider_handle_width" />
-    <corners android:radius="1dp" />
-    <solid android:color="@color/docked_divider_handle" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
new file mode 100644
index 0000000..ea4db4b
--- /dev/null
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="16dp"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="32dp" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toStartOf="@android:id/toggle"
+        android:layout_toEndOf="@android:id/icon"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
+        android:text="@string/battery_detail_switch_title" />
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_toStartOf="@android:id/toggle"
+        android:layout_toEndOf="@android:id/icon"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary"
+        android:text="@string/battery_detail_switch_summary" />
+
+    <Switch
+        android:id="@android:id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginEnd="16dp"
+        android:clickable="false"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..f7f673d
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@drawable/system_bar_background">
+
+    <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
+         rotation so skip this level of the heirarchy.
+    -->
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:orientation="horizontal"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:id="@+id/nav_buttons"
+        android:animateLayoutChanges="true">
+
+        <!-- Buttons get populated here from a car_arrays.xml. -->
+    </LinearLayout>
+
+    <!-- lights out layout to match exactly -->
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:orientation="horizontal"
+        android:id="@+id/lights_out"
+        android:visibility="gone">
+        <!-- Must match nav_buttons. -->
+    </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml
new file mode 100644
index 0000000..87c8f04
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_navigation_button.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationButton
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center">
+    <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_icon"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:layout_centerInParent="true"
+            android:animateLayoutChanges="true">
+    </com.android.keyguard.AlphaOptimizedImageButton>
+
+    <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_more_icon"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toRightOf="@+id/car_nav_button_icon"
+            android:animateLayoutChanges="true">
+    </com.android.keyguard.AlphaOptimizedImageButton>
+</com.android.systemui.statusbar.car.CarNavigationButton>
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/packages/SystemUI/res/layout/docked_stack_divider.xml
index 22ed216..7ea5027 100644
--- a/packages/SystemUI/res/layout/docked_stack_divider.xml
+++ b/packages/SystemUI/res/layout/docked_stack_divider.xml
@@ -24,10 +24,9 @@
         android:id="@+id/docked_divider_background"
         android:background="@color/docked_divider_background"/>
 
-    <ImageButton
+    <com.android.systemui.stackdivider.DividerHandleView
         style="@style/DockedDividerHandle"
         android:id="@+id/docked_divider_handle"
-        android:background="@null"
-        android:src="@drawable/docked_divider_handle"/>
+        android:background="@null"/>
 
 </com.android.systemui.stackdivider.DividerView>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
new file mode 100644
index 0000000..460433e
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyboard_shortcuts_wrapper"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="40dp"
+    android:focusable="true">
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 3cdee64..03451b4 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -25,57 +25,39 @@
         android:gravity="top|start"
         android:orientation="vertical"
         android:paddingStart="@*android:dimen/notification_content_margin_start"
-        android:paddingEnd="@*android:dimen/notification_content_margin_end"
-        android:background="@color/notification_guts_text_color" >
+        android:paddingEnd="8dp"
+        android:background="@color/notification_guts_bg_color" >
 
     <!-- header -->
-    <FrameLayout
+    <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="8dp" >
+            android:layout_height="30dp"
+            android:paddingTop="9dp"
+            android:paddingEnd="8dp"
+            android:id="@+id/notification_guts_header"
+            android:orientation="horizontal"
+            android:layout_gravity="center_vertical|start">
 
-        <LinearLayout
+        <ImageView
+                android:id="@android:id/icon"
+                android:layout_width="18dp"
+                android:layout_height="18dp"
+                android:layout_marginEnd="6dp"
+                android:src="@android:drawable/arrow_down_float" />
+        <TextView
+                android:id="@+id/pkgname"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:id="@+id/notification_guts_header"
-                android:orientation="horizontal"
-                android:layout_gravity="center_vertical|start"
-                android:layout_marginEnd="52dp">
-                    <ImageView
-                            android:id="@android:id/icon"
-                            android:layout_width="18dp"
-                            android:layout_height="18dp"
-                            android:layout_marginEnd="3dp"
-                            android:src="@android:drawable/arrow_down_float" />
-                    <TextView
-                            android:id="@+id/pkgname"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:textAppearance="@android:style/TextAppearance.Material.Notification.Info"
-                            android:layout_marginStart="3dp"
-                            android:layout_marginEnd="4dp"
-                            android:textColor="@color/notification_guts_title_color" />
-                    <TextView
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:id="@+id/debug_info"
-                            android:layout_weight="0"
-                            android:textAppearance="@android:style/TextAppearance.Material.Notification.Time"
-                            android:layout_gravity="bottom|start"
-                            android:visibility="gone"
-                            android:textColor="#ffffff" />
-        </LinearLayout>
-
-        <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
-                android:id="@+id/notification_inspect_item"
-                android:layout_width="52dp"
+                style="@style/TextAppearance.NotificationGuts.Header" />
+        <TextView
+                android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:id="@+id/debug_info"
                 android:layout_weight="0"
-                android:gravity="center"
-                android:layout_gravity="center_vertical|end"
-                android:contentDescription="@string/status_bar_notification_inspect_item_title"
-                android:src="@drawable/ic_settings" />
-    </FrameLayout>
+                style="@style/TextAppearance.NotificationGuts.Header"
+                android:layout_gravity="bottom|start"
+                android:visibility="gone" />
+    </LinearLayout>
     <!-- Importance slider -->
     <LinearLayout
             android:layout_width="match_parent"
@@ -84,16 +66,17 @@
             android:orientation="vertical"
             android:clickable="false"
             android:focusable="false"
-            android:paddingBottom="8dip">
+            android:paddingBottom="8dip"
+            android:paddingEnd="8dp" >
         <TextView
                 android:id="@+id/title"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:singleLine="true"
-                android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-                android:textColor="@color/notification_guts_text_color"
+                style="@style/TextAppearance.NotificationGuts.Primary"
                 android:ellipsize="marquee"
-                android:fadingEdge="horizontal"/>
+                android:fadingEdge="horizontal"
+                android:paddingBottom="2dp"/>
 
         <TextView
                 android:id="@+id/summary"
@@ -101,38 +84,41 @@
                 android:layout_height="wrap_content"
                 android:layout_alignStart="@android:id/title"
                 android:textAlignment="viewStart"
-                android:textAppearance="@android:style/TextAppearance.Material.Body1"
-                android:textColor="@color/notification_guts_title_color"
+                style="@style/TextAppearance.NotificationGuts.Secondary"
                 android:maxLines="3"
-                android:minLines="2" />
+                android:minLines="2"
+                android:paddingBottom="4dp" />
 
         <FrameLayout
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
+                android:layout_height="48dp"
                 android:paddingTop="8dp" >
 
             <ImageView
                     android:id="@+id/low_importance"
-                    android:src="@android:drawable/ic_menu_close_clear_cancel"
+                    android:src="@*android:drawable/ic_notification_block"
                     android:layout_gravity="center_vertical|start"
                     android:layout_width="24dp"
                     android:layout_height="24dp" />
 
             <SeekBar
                     android:id="@+id/seekbar"
-                    android:layout_marginStart="24dp"
-                    android:layout_marginEnd="24dp"
+                    android:layout_marginStart="56dp"
+                    android:layout_marginEnd="56dp"
                     android:layout_gravity="center_vertical"
                     android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
+                    android:layout_height="48dp"
                     android:focusable="true"
                     android:background="#00ffffff"
-                    android:thumbTint="@android:color/white"
-                    android:progressTint="@android:color/white" />
+                    android:progressBackgroundTint="@color/notification_guts_secondary_slider_color"
+                    android:thumbTint="@color/notification_guts_slider_color"
+                    android:progressTint="@color/notification_guts_slider_color"
+                    style="@android:style/Widget.Material.SeekBar.Discrete"
+                    android:tickMarkTint="@android:color/black" />
 
             <ImageView
                     android:id="@+id/max_importance"
-                    android:src="@android:drawable/ic_popup_reminder"
+                    android:src="@*android:drawable/ic_notification_alert"
                     android:layout_gravity="center_vertical|end"
                     android:layout_width="24dp"
                     android:layout_height="24dp"/>
@@ -142,19 +128,53 @@
         <RadioGroup
                     android:id="@+id/apply_to"
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:paddingTop="8dp">
+                    android:layout_height="wrap_content" >
             <RadioButton android:id="@+id/apply_to_topic"
                          android:layout_width="wrap_content"
-                         android:layout_height="wrap_content"
-                         android:textColor="@color/notification_guts_text_color"
-                         android:visibility="gone"/>
+                         android:layout_height="48dp"
+                         style="@style/TextAppearance.NotificationGuts.Primary"
+                         android:visibility="gone"
+                         android:buttonTint="#858383"
+            />
             <RadioButton android:id="@+id/apply_to_app"
                          android:layout_width="wrap_content"
-                         android:layout_height="wrap_content"
+                         android:layout_height="48dp"
                          android:text="@string/apply_to_app"
-                         android:textColor="@color/notification_guts_text_color"
-                         android:visibility="gone"/>
+                         style="@style/TextAppearance.NotificationGuts.Primary"
+                         android:visibility="gone"
+                         android:buttonTint="#858383"
+            />
         </RadioGroup>
     </LinearLayout>
+    <!-- buttons -->
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:paddingBottom="8dp" >
+
+        <TextView
+            android:id="@+id/more_settings"
+            android:text="@string/notification_more_settings"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            style="@style/TextAppearance.NotificationGuts.Button"
+            android:background="@drawable/btn_borderless_rect"
+            android:gravity="center"
+            android:paddingEnd="8dp"
+            android:paddingStart="8dp"
+            android:focusable="true" />
+
+        <TextView
+            android:id="@+id/done"
+            android:text="@string/notification_done"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            style="@style/TextAppearance.NotificationGuts.Button"
+            android:background="@drawable/btn_borderless_rect"
+            android:gravity="center"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:focusable="true"/>
+    </LinearLayout>
 </com.android.systemui.statusbar.NotificationGuts>
diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml
new file mode 100644
index 0000000..c5b4e84
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_icon_area.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<com.android.keyguard.AlphaOptimizedLinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_icon_area_inner"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <com.android.systemui.statusbar.StatusBarIconView
+        android:id="@+id/moreIcon"
+        android:layout_width="@dimen/status_bar_icon_size"
+        android:layout_height="match_parent"
+        android:src="@drawable/stat_notify_more"
+        android:visibility="gone" />
+    <com.android.systemui.statusbar.phone.IconMerger
+        android:id="@+id/notificationIcons"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentStart="true"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"/>
+</com.android.keyguard.AlphaOptimizedLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
index 6facb71..ccdddf7 100644
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_detail_item.xml
@@ -60,6 +60,7 @@
         android:clickable="true"
         android:focusable="true"
         android:scaleType="center"
+        android:contentDescription="@*android:string/media_route_controller_disconnect"
         android:src="@drawable/ic_qs_cancel" />
 
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
new file mode 100644
index 0000000..603ebbf
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+     <TextView android:id="@+id/tile_label"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:textColor="@color/qs_tile_text"
+             android:gravity="center_horizontal"
+             android:minLines="2"
+             android:padding="0dp"
+             android:fontFamily="sans-serif-condensed"
+             android:textStyle="normal"
+             android:textSize="@dimen/qs_tile_text_size"
+             android:clickable="false" />
+     <ImageView android:id="@+id/restricted_padlock"
+             android:layout_width="@dimen/qs_tile_text_size"
+             android:layout_height="@dimen/qs_tile_text_size"
+             android:src="@drawable/ic_settings_lock_outline"
+             android:layout_marginLeft="@dimen/restricted_padlock_pading"
+             android:baselineAlignBottom="true"
+             android:scaleType="centerInside"
+             android:visibility="gone" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index af22f03..a22c360 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -40,12 +40,25 @@
             systemui:framePadding="6dp"
             systemui:activeFrameColor="@color/current_user_border_color"/>
 
-    <TextView
-            android:id="@+id/user_name"
+    <LinearLayout
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textSize="@dimen/qs_detail_item_secondary_text_size"
-            android:textColor="@color/qs_user_detail_name"
-            android:gravity="center_horizontal" />
+            android:layout_height="wrap_content">
+        <TextView
+                android:id="@+id/user_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="@dimen/qs_detail_item_secondary_text_size"
+                android:textColor="@color/qs_user_detail_name"
+                android:gravity="center_horizontal" />
+        <ImageView
+                android:id="@+id/restricted_padlock"
+                android:layout_width="@dimen/qs_detail_item_secondary_text_size"
+                android:layout_height="@dimen/qs_detail_item_secondary_text_size"
+                android:src="@drawable/ic_settings_lock_outline"
+                android:layout_marginLeft="@dimen/restricted_padlock_pading"
+                android:baselineAlignBottom="true"
+                android:scaleType="centerInside"
+                android:visibility="gone" />
+    </LinearLayout>
 
 </com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 2b82b05..c813818 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -34,9 +34,11 @@
             android:layout_gravity="bottom|right"
             android:layout_marginRight="15dp"
             android:layout_marginBottom="15dp"
-            android:translationZ="2dp"
+            android:translationZ="4dp"
             android:contentDescription="@string/recents_lock_to_app_button_label"
-            android:background="@drawable/recents_lock_to_task_button_bg">
+            android:background="@drawable/recents_lock_to_task_button_bg"
+            android:visibility="invisible"
+            android:alpha="0">
             <ImageView
                 android:layout_width="@dimen/recents_lock_to_app_icon_size"
                 android:layout_height="@dimen/recents_lock_to_app_icon_size"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 04f18c5..07ac39a 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+     Copyright (C) 2014 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      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.
@@ -63,4 +64,12 @@
         android:background="@drawable/recents_button_bg"
         android:visibility="invisible"
         android:src="@drawable/recents_dismiss_light" />
+    <ProgressBar
+        android:id="@+id/focus_timer_indicator"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="5dp"
+        android:layout_gravity="bottom"
+        android:indeterminateOnly="false"
+        android:visibility="invisible" />
 </com.android.systemui.recents.views.TaskViewHeader>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 83cfc76..cce07bd 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -23,16 +23,16 @@
         android:id="@+id/remote_input"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
-        android:paddingStart="16dp"
-        android:paddingEnd="12dp"
-        android:paddingBottom="4dp"
-        android:paddingTop="2dp">
+        android:paddingEnd="12dp">
 
     <view class="com.android.systemui.statusbar.policy.RemoteInputView$RemoteEditText"
             android:id="@+id/remote_input_text"
             android:layout_height="match_parent"
             android:layout_width="0dp"
             android:layout_weight="1"
+            android:paddingTop="2dp"
+            android:paddingBottom="4dp"
+            android:paddingStart="16dp"
             android:paddingEnd="12dp"
             android:gravity="start|center_vertical"
             android:textAppearance="?android:attr/textAppearance"
@@ -47,7 +47,9 @@
     <FrameLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical">
+            android:layout_gravity="center_vertical"
+            android:paddingTop="2dp"
+            android:paddingBottom="4dp">
 
         <ImageButton
                 android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index aaa5a09..bea9f78 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -47,34 +47,14 @@
         android:orientation="horizontal"
         >
 
+        <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+             PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
         <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
             android:id="@+id/notification_icon_area"
             android:layout_width="0dip"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            android:orientation="horizontal"
-            >
-            <!-- The alpha of this area is both controlled from PhoneStatusBarTransitions and
-                 PhoneStatusBar (DISABLE_NOTIFICATION_ICONS), so we need two views here. -->
-            <com.android.keyguard.AlphaOptimizedLinearLayout
-                android:id="@+id/notification_icon_area_inner"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                >
-                <com.android.systemui.statusbar.StatusBarIconView android:id="@+id/moreIcon"
-                    android:layout_width="@dimen/status_bar_icon_size"
-                    android:layout_height="match_parent"
-                    android:src="@drawable/stat_notify_more"
-                    android:visibility="gone"
-                    />
-                <com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:layout_alignParentStart="true"
-                    android:gravity="center_vertical"
-                    android:orientation="horizontal"/>
-            </com.android.keyguard.AlphaOptimizedLinearLayout>
-        </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+            android:orientation="horizontal" />
 
         <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 7ac9c41..39da8d0 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -84,16 +84,10 @@
               android:layout_width="match_parent"
               android:layout_height="match_parent"/>
 
-    <com.android.systemui.statusbar.phone.PanelHolder
-        android:id="@+id/panel_holder"
+    <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/transparent" >
-        <include layout="@layout/status_bar_expanded"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" />
-    </com.android.systemui.statusbar.phone.PanelHolder>
+        android:visibility="gone" />
 
     <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
new file mode 100644
index 0000000..a638d17
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:layout_alignParentEnd="true"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingStart="10dp"
+        android:paddingEnd="10dp"
+        android:background="#88FFFFFF">
+        <LinearLayout
+            android:orientation="vertical"
+            android:layout_gravity="center_vertical"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" >
+
+            <Button android:id="@+id/full"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/pip_fullscreen"
+                android:textSize="10sp"
+                android:focusable="true" />
+
+            <Button android:id="@+id/exit"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/pip_exit"
+                android:textSize="10sp"
+                android:focusable="true" />
+
+            <Button android:id="@+id/cancel"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/pip_cancel"
+                android:textSize="10sp"
+                android:focusable="true" />
+        </LinearLayout>
+    </FrameLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
new file mode 100644
index 0000000..e8691b5
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/guide_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:padding="3dp"
+        android:textSize="13sp"
+        android:textColor="#111111"
+        android:background="#99EEEEEE"
+        android:text="@string/pip_hold_home" />
+</FrameLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index f81bf23..19fb28e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Wys hierdie kennisgewings sonder geluide"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Wys boaan die kennisgewinglys en maak \'n geluid"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Verskyn vlugtig op die skerm en maak \'n geluid"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Meer instellings"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Klaar"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normale kleure"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Aandkleure"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Gepasmaakte kleure"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Pas toe"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Bevestig instellings"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Sommige kleurinstellings kan hierdie toestel onbruikbaar maak. Klik OK om hierdie kleurinstellings te bevestig; andersins sal hierdie instellings ná 10 sekondes teruggestel word."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterybespaarder is nie beskikbaar wanneer gelaai word nie"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterybespaarder"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Verminder werkverrigting en agtergronddata"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index d96e790..208aaff 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"እነዚህን ማሳወቂያዎች በጸጥታ አሳይ"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"በማሳወቂያዎች ዝርዝር ላይኛው ክፍል ላይ አሳይና ድምፅ አሰማ"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"ወደ ገጸ ማያው ይመልከቱና ድምፅ ይቅረጹ"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string>
+    <string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"መደበኛ ቀለሞች"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"የለሊት ቀለሞች"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ብጁ ቀለሞች"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"ተግብር"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ቅንብሮችን ያረጋግጡ"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"አንዳንድ የቀለም ቅንብሮች ይህን መሣሪያ የማይጠቅም ሊያደርጉት ይችላሉ። እነዚህን የቀለም ቅንብሮች ለማረጋገጥ እሺ የሚለውን ጠቅ ያድርጉ፣ አለበለዚያ እነዚህ ቅንብሮች ከ10 ሰከንዶች በኋላ ዳግም ይጀምራሉ።"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ባትሪ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ኃይል በሚሞላበት ጊዜ ባትሪ ቆጣቢ አይገኝም"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ባትሪ ቆጣቢ"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"አፈጻጸምን እና የጀርባ ውሂብን ይቀንሳል"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b3e494c..35e78c2 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -464,6 +464,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"عرض هذه الإشعارات بدون تنبيه صوتي"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"العرض أعلى قائمة الإشعارات مع تنبيه صوتي"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"الظهور سريعًا على الشاشة مع تنبيه صوتي"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"المزيد من الإعدادات"</string>
+    <string name="notification_done" msgid="5279426047273930175">"تم"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ألوان عادية"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ألوان ليلية"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ألوان مخصصة"</string>
@@ -474,4 +476,8 @@
     <string name="color_apply" msgid="9212602012641034283">"تطبيق"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"تأكيد الإعدادات"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"يمكن أن تتسبب بعض إعدادات الألوان في تعطيل إمكانية استخدام الجهاز. يمكنك النقر على \"موافق\" لتأكيد إعدادات الألوان هذه، وإلا فستتم إعادة تعيين هذه الإعدادات بعد 10 ثوانٍ."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"البطارية (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"وضع توفير شحن البطارية غير متاح أثناء الشحن."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"توفير شحن البطارية"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"لخفض مستوى الأداء وبيانات الخلفية"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 4efc5df..864fd32 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Bu bildişləri səssiz göstərin"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Bildirişlər siyahısında yuxarıda göstərin və səsli edin"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Ekranda nəzər salın və səsli edin"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Daha çox ayar"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Hazırdır"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normal rənglər"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Gecə rəngləri"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Xüsusi rənglər"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Tətbiq edin"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Ayarları təsdiq edin"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Bəzi renk ayarları bu cihazı yararsız edə bilər. Bu rənk ayarlarını təsdiq etmək üçün OK basın, əks halda bu ayarlar 10 saniyə sonra sıfırlanacaq."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Enerji (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Enerji Qənaəti doldurulma zamanı əlçatan deyil"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Enerji Qənaəti"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Performansı azaldır və arxa fon datasını məhdudlaşdırır"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f482e96..6879d75 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -455,12 +455,14 @@
     <string name="low_importance" msgid="4109929986107147930">"Mala važnost"</string>
     <string name="default_importance" msgid="8192107689995742653">"Uobičajena važnost"</string>
     <string name="high_importance" msgid="1527066195614050263">"Velika važnost"</string>
-    <string name="max_importance" msgid="5089005872719563894">"Najveća važnost"</string>
+    <string name="max_importance" msgid="5089005872719563894">"Važnost: hitno"</string>
     <string name="notification_importance_blocked" msgid="2397192642657872872">"Ova obaveštenja se nikada ne prikazuju"</string>
     <string name="notification_importance_low" msgid="4383563267370859725">"Prikazuju se u dnu liste obaveštenja bez zvuka"</string>
     <string name="notification_importance_default" msgid="4926529615920610817">"Ova obaveštenja se prikazuju bez zvuka"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Prikazuju se u vrhu liste obaveštenja i emituje se zvuk"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Nakratko se prikazuju na ekranu i emituje se zvuk"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Još podešavanja"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normalne boje"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Noćne boje"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Prilagođene boje"</string>
@@ -471,4 +473,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Primeni"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Potvrdite podešavanja"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Neka podešavanja boja mogu da učine uređaj neupotrebljivim. Kliknite na Potvrdi da biste potvrdili ova podešavanja boja, pošto će se u suprotnom ova podešavanja resetovati nakon 10 sekundi."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Baterija (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ušteda baterije nije dostupna tokom punjenja"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ušteda baterije"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Smanjuje performanse i pozadinske podatke"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 95c4a22..f5713c2 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Тези известия се показват без звук"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Показване най-горе в списъка с известия и издаване на звуков сигнал"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Показване на екрана и издаване на звуков сигнал"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Още настройки"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Нормални цветове"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Нощни цветове"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Персонализирани цветове"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Прилагане"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Потвърждаване на настройките"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Някои настройки за цветовете могат да направят това устройство неизползваемо. За да ги потвърдите, кликнете върху „OK“. В противен случай те ще бъдат нулирани след 10 секунди."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Батерия (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режимът за запазване на батерията не е налице при зареждане"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим за запазване на батерията"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Намалява ефективността и данните на заден план"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 38885d5..bfcb258 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"নিঃশব্দে এই বিজ্ঞপ্তিগুলি দেখানো হয়"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"বিজ্ঞপ্তি তালিকার শীর্ষে দেখানো হয় এবং শব্দ করে"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"স্ক্রীনের উপরে দেখানো হয় এবং শব্দ করে"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"আরো সেটিংস"</string>
+    <string name="notification_done" msgid="5279426047273930175">"সম্পন্ন"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"স্বাভাবিক রঙ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"রাতের রঙ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"কাস্টম রঙ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"প্রয়োগ করুন"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"সেটিংস নিশ্চিত করুন"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"কিছু রঙের সেটিংস এই ডিভাইসকে ব্যবহারের অযোগ্য করে দিতে পারে৷ এই রঙের সেটিংস নিশ্চিত করতে ওকে এ ক্লিক করুন, অন্যথায় ১০ সেকেন্ড পরে এই সেটিংস পুনরায় সেট হবে৷"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ব্যাটারি (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"চার্জ করার সময় ব্যাটারি সেভার উপলব্ধ নয়"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ব্যাটারি সেভার"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index d27c3ee..c7a30f0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostra aquestes notificacions de manera silenciosa"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostra a la part superior de la llista de notificacions i emet un so"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostra a la pantalla i emet un so"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Més opcions"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Fet"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Colors normals"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Colors nocturns"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Colors personalitzats"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplica"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirma la configuració"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algunes opcions de configuració de color poden deixar el dispositiu inservible. Fes clic a D\'acord per confirmar la configuració de color; en cas contrari, la configuració es restablirà al cap de 10 segons."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la càrrega"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Estalvi de bateria"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Redueix el rendiment i les dades en segon pla"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4f26dc1..df18347 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Tato oznámení zobrazovat bez zvukového upozornění"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Tato oznámení zobrazovat na začátku seznamu a upozornit na ně zvukem"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Tato oznámení zobrazovat přímo na obrazovce a upozornit na ně zvukem"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Další nastavení"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normální barvy"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Noční barvy"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Vlastní barvy"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Použít"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Ověření nastavení"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Některá nastavení barev mohou způsobit, že zařízení nebude použitelné. Kliknutím na OK toto nastavení barev potvrdíte, v opačném případě se nastavení po 10 sekundách resetuje."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Baterie (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Spořič baterie při nabíjení není k dispozici."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Spořič baterie"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Omezuje výkon a data na pozadí"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 3f868a8..d69e943 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Vis disse underretninger lydløst"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Vis øverst på listen over underretninger, og giv lyd"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Vis på skærmen, og giv lyd"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Færdig"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Almindelige farver"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nattefarver"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Tilpassede farver"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Anvend"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Bekræft indstillingerne"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Nogle farveindstillinger kan medføre, at du ikke kan bruge enheden. Klik på OK for at bekræfte disse farveindstillinger. Ellers nulstilles disse indstillinger efter ti sekunder."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batteri (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparefunktionen er ikke tilgængelig under opladning"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparefunktion"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reducerer ydeevne og baggrundsdata"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4c87758..9e17f253 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -35,7 +35,7 @@
     <string name="battery_low_title" msgid="6456385927409742437">"Akku ist schwach"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
     <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend. Der Energiesparmodus ist aktiviert."</string>
-    <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt.\nVerwenden Sie das mitgelieferte Aufladegerät."</string>
+    <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt.\nVerwende das mitgelieferte Aufladegerät."</string>
     <string name="invalid_charger_title" msgid="3515740382572798460">"Laden per USB wird nicht unterstützt."</string>
     <string name="invalid_charger_text" msgid="5474997287953892710">"Verwenden Sie nur das im Lieferumfang enthaltene Ladegerät."</string>
     <string name="battery_low_why" msgid="4553600287639198111">"Einstellungen"</string>
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Diese Benachrichtigungen ohne Ton anzeigen"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mit Ton ganz oben in der Benachrichtigungsliste anzeigen"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mit Ton auf dem Display einblenden"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Fertig"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Standardfarben"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nachtfarben"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Benutzerdefinierte Farben"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Übernehmen"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Einstellungen bestätigen"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Einige Farbeinstellungen können dazu führen, dass das Gerät nicht mehr genutzt werden kann. Klicke auf \"OK\", um diese Farbeinstellungen zu bestätigen. Anderenfalls werden diese Einstellungen in 10 Sekunden zurückgesetzt."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Akku (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Energiesparmodus"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduzierung der Leistung und Hintergrunddaten"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 21e1eee..fb09669 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Να εμφανίζονται αυτές οι ειδοποιήσεις χωρίς ήχο"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Να εμφανίζονται στην κορυφή της λίστας ειδοποιήσεων με ήχο"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Να προβάλλονται στην οθόνη και να συνοδεύονται από κάποιον ήχο"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Περισσότερες ρυθμίσεις"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Τέλος"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Κανονικά χρώματα"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Νυχτερινά χρώματα"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Προσαρμοσμένα χρώματα"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Εφαρμογή"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Επιβεβαίωση ρυθμίσεων"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Ορισμένες ρυθμίσεις χρωμάτων μπορεί να μην επιτρέπουν τη χρήση αυτής της συσκευής. Κάντε κλικ στην επιλογή OK για να επιβεβαιώσετε αυτές τις ρυθμίσεις χρωμάτων, διαφορετικά θα γίνει επαναφορά αυτών των ρυθμίσεων μετά από 10 δευτερόλεπτα."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Μπαταρία (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Η εξοικονόμηση μπαταρίας δεν είναι διαθέσιμη κατά τη διάρκεια της φόρτισης"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Εξοικονόμηση μπαταρίας"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Μειώνει την απόδοση και τα δεδομένα παρασκηνίου"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fa8b6c33..a96c23c 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Silently show these notifications"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Show at the top of the notifications list and make sound"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Peek onto the screen and make sound"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Finished"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normal colours"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Night colours"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Customised colours"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Apply"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fa8b6c33..a96c23c 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Silently show these notifications"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Show at the top of the notifications list and make sound"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Peek onto the screen and make sound"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Finished"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normal colours"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Night colours"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Customised colours"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Apply"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fa8b6c33..a96c23c 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Silently show these notifications"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Show at the top of the notifications list and make sound"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Peek onto the screen and make sound"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"More settings"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Finished"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normal colours"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Night colours"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Customised colours"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Apply"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 54850f0..6ba8a43 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificaciones de manera silenciosa"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar en la parte superior de la lista de notificaciones y emitir sonido"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar en la pantalla y emitir sonido"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Más opciones de configuración"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Listo"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Colores normales"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Colores nocturnos"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Colores personalizados"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmar la configuración"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algunas opciones de configuración de color pueden provocar que el dispositivo quede inutilizable. Haz clic en Aceptar para confirmar estos parámetros de color. De lo contrario, la configuración se restablecerá en diez segundos."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batería (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no está disponible durante la carga"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y el uso de datos en segundo plano"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 41d83dc..0598078 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificaciones de forma silenciosa"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar en la parte superior de la lista de notificaciones y emitir sonido"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar en la pantalla y emitir sonido"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Más ajustes"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Listo"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Colores normales"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Colores nocturnos"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Colores personalizados"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configuración"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algunas opciones de configuración de color pueden hacer que el dispositivo no se pueda utilizar. Haz clic en Aceptar para confirmar esta configuración. Si no lo haces, se restablecerá esta configuración cuando transcurran 10 segundos."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batería (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no disponible mientras se carga el dispositivo"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y las conexiones automáticas"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 9bb7898..8d25d48 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Kuva need märguanded vaikselt"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Kuva märguannete loendi ülaosas koos heliga"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Kuva ekraani servas koos heliga"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Rohkem seadeid"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Tavalised värvid"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Öised värvid"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Kohandatud värvid"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Rakenda"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Seadete kinnitamine"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Mõni värviseade ei saa seadet võib-olla kasutada. Nende värviseadete kinnitamiseks klõpsake OK, muidu lähtestatakse need seaded 10 sekundi pärast."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Aku (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akusäästja pole laadimise ajal saadaval"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akusäästja"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vähendab jõudlust ja taustaandmeid"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index e62303a..fc96b42 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Erakutsi jakinarazpen hauek, baina soinurik egin gabe"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Erakutsi jakinarazpen hauek zerrendaren goialdean eta egin soinua"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Agerrarazi jakinarazpen hauek pantailan eta egin soinua"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Eginda"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Kolore normalak"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Gaueko koloreak"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Kolore pertsonalizatuak"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplikatu"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Berretsi ezarpenak"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Baliteke gailua kolore-ezarpen batzuekin ezin erabili izatea. Kolore-ezarpenak berresteko, sakatu Ados. Bestela, hamar segundoren buruan berrezarriko dira ezarpenak."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (%% <xliff:g id="ID_1">%1$d</xliff:g>)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Bateria-aurrezlea ez dago erabilgarri gailua kargatzen ari denean"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Bateria-aurrezlea"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Errendimendua eta atzeko planoko datuen erabilera murrizten ditu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f45c01b..3a98e9c 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"این اعلان‌ها بی‌صدا نشان داده شوند"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"در بالای فهرست اعلان‌ها و به همراه صدا نشان داده شود"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"در جلوی صفحه به همراه صدا نشان داده شود"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"تنظیمات بیشتر"</string>
+    <string name="notification_done" msgid="5279426047273930175">"تمام"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"رنگ‌های عادی"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"رنگ‌های شب"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"رنگ‌های سفارشی"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"اعمال‌ کردن"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"تأیید تنظیمات"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"بعضی از تنظیمات رنگ می‌توانند این دستگاه را غیرقابل استفاده کنند. برای تأیید این تنظیمات رنگ روی «تأیید» کلیک کنید، در غیر این صورت این تغییرات بعد از ۱۰ ثانیه بازنشانی می‌شوند."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"باتری (<xliff:g id="ID_1">%1$d</xliff:g>٪٪)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"هنگام شارژ شدن، «بهینه‌سازی باتری» در دسترس نیست"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"بهینه‌سازی باتری"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"عملکرد و اطلاعات پس‌زمینه را کاهش می‌دهد"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index af1a1e5..8c963af 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Näytä nämä ilmoitukset huomaamattomasti"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Näytä ilmoitukset luettelon kärjessä ja toista merkkiääni"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Näytä ilmoitus näytöllä ja toista äänimerkki"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Lisäasetukset"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Tavalliset värit"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Yövärit"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Muokatut värit"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Käytä"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Vahvista asetukset"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Jotkin väriasetukset voivat häiritä laitteen käyttöä. Vahvista uudet väriasetukset valitsemalla OK. Muussa tapauksessa aiemmat asetukset palautetaan 10 sekunnin kuluttua."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Akku (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Virransäästö ei ole käytettävissä latauksen aikana."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Virransäästö"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Rajoittaa suorituskykyä ja taustatiedonsiirtoa."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 20a8bdc..bcde17a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Afficher ces notifications en mode silencieux"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Afficher en haut de la liste des notifications et émettre un son"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Afficher sur l\'écran et émettre un son"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Couleurs normales"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Couleurs nocturnes"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Couleurs personnalisées"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Appliquer"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmer les paramètres"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Certains paramètres de couleurs peuvent rendre cet appareil inutilisable. Cliquez sur « OK » pour valider ces paramètres, sinon ils seront réinitialisés après 10 secondes."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Pile (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Le mode Économie d\'énergie n\'est pas accessible pendant la charge"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Économie d\'énergie"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Réduit les performances et les données en arrière-plan"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 8e76ee8..952baab 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Afficher ces notifications en mode silencieux"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Afficher en haut de la liste des notifications et émettre un son"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Afficher sur l\'écran et émettre un son"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Couleurs normales"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Couleurs nocturnes"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Couleurs personnalisées"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Appliquer"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Vérifier les paramètres"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Certains paramètres de couleurs peuvent rendre cet appareil inutilisable. Cliquez sur \"OK\" pour valider ces paramètres, sans quoi ils seront réinitialisés après 10 secondes."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batterie (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"L\'économiseur de batterie n\'est pas disponible lorsque l\'appareil est en charge."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Économiseur de batterie"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Limite les performances et les données en arrière-plan."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 4562d89b..9d490c2 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificacións de forma silenciosa"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificacións e emitir son"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar na pantalla e emitir son"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Máis opcións"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Feito"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Cores nocturnas"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configuración"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algunhas opcións de configuración de cor poden facer que este dispositivo sexa inutilizable. Fai clic en Aceptar para confirmar esta configuración de cor; en caso contrario, a configuración restablecerase tras 10 segundos."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batería (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"A función aforro de batería non está dispoñible durante a carga"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Aforro de batería"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce o rendemento e os datos en segundo plano"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 69174ef..d45167f 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"આ સૂચનાઓ ચુપચાપ બતાવો"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"સૂચનાઓની સૂચિની ટોચ પર બતાવો અને અવાજ કરો"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"સ્ક્રીન પર ત્વરિત દ્રષ્ટિ કરો અને અવાજ કરો"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"વધુ સેટિંગ્સ"</string>
+    <string name="notification_done" msgid="5279426047273930175">"થઈ ગયું"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"સામાન્ય રંગો"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"રાત્રિ રંગો"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"કસ્ટમ રંગો"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"લાગુ કરો"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"સેટિંગ્સની પુષ્ટિ કરો"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"કેટલીક રંગ સેટિંગ્સ આ ઉપકરણને બિનઉપયોગી બનાવી શકે છે. આ રંગ સેટિંગ્સની પુષ્ટિ કરવા માટે ઑકે ક્લિક કરો, અન્યથા 10 સેકંડ પછી આ સેટિંગ્સ ફરીથી સેટ થશે."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"બૅટરી (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ચાર્જિંગ દરમિયાન બૅટરી બચતકર્તા ઉપલબ્ધ નથી"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"બૅટરી બચતકર્તા"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"પ્રદર્શન અને પૃષ્ઠભૂમિ ડેટા ઘટાડે છે"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1ab7f71..3e2ab2a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ये नोटिफिकेशन मौन रूप से दिखाएं"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"नोटिफिकेशन सूची में सबसे ऊपर दिखाएं और ध्वनि चलाएं"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"स्‍क्रीन पर एक झलक दिखाएं और ध्‍वनि चलाएं"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"और सेटिंग"</string>
+    <string name="notification_done" msgid="5279426047273930175">"हो गया"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"सामान्य रंग"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"रात्रि के रंग"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"कस्टम रंग"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"लागू करें"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"से‍ेटिंग की पुष्टि करें"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"कुछ रंग सेटिंग इस डिवाइस को अनुपयोगी बना सकती हैं. इन रंग सेटिंग की पुष्टि करने के लिए ठीक क्लिक करें, अन्यथा 10 सेकंड के बाद ये सेटिंग रीसेट हो जाएंगी."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"बैटरी (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज किए जाने के दौरान बैटरी सेवर उपलब्ध नहीं है"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"बैटरी सेवर"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"निष्‍पादन और पृष्ठभूमि डेटा को कम करता है"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index a67258c..fbdc5fe 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -461,6 +461,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Prikaži te obavijesti tiho"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Prikaži na vrhu popisa obavijesti i emitiraj zvučni signal"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Prikaži na zaslonu i emitiraj zvučni signal"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Uobičajene boje"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Noćne boje"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Prilagođene boje"</string>
@@ -471,4 +473,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Primijeni"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Potvrdite postavke"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Neke postavke boja mogu učiniti uređaj neupotrebljivim. Kliknite U redu da biste potvrdili postavke boja jer će se u suprotnom poništiti za 10 sekundi."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Baterija (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Štednja baterije nije dostupna tijekom punjenja"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Štednja baterije"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Smanjuje količinu rada i pozadinske podatke"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 8589e95..9d26c36 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Hang nélkül jelennek meg ezek az értesítések"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Az értesítési lista tetején jelennek meg hangjelzéssel"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Az értesítések felugranak a képernyőn hangjelzéssel"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"További beállítások"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Kész"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Hagyományos színek"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Esti színek"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Egyéni színek"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Alkalmaz"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Beállítások megerősítése"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Bizonyos színbeállítások használhatatlanná tehetik ezt az eszközt. A színbeállítás megerősítéséhez kattintson az OK lehetőségre, máskülönben a rendszer 10 másodpercen belül visszaáll a korábbira."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Akkumulátor (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Az Akkumulátorkímélő módot töltés közben nem lehet használni"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akkumulátorkímélő mód"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Csökkenti a teljesítményt és a háttéradatok használatát"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 1b8de04..29e3a29 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Ցուցադրել այս ծանուցումներն առանց ձայնային ազդանշանի"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Ցուցադրել ծանուցումների ցանկի վերևում և հնչեցնել ձայնային ազդանշան"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Ցուցադրել էկրանին և հնչեցնել ձայնային ազդանշան"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Այլ կարգավորումներ"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Պատրաստ է"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Սովորական գույներ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Գիշերային գույներ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Հատուկ գույներ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Կիրառել"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Հաստատել կարգավորումները"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Գունային որոշ կարգավորումները կարող են այս սարքը օգտագործման համար ոչ պիտանի դարձնել: Սեղմեք Լավ կոճակը՝ գունային այս կարգավորումները հաստատելու համար: Հակառակ դեպքում այս կարգավորումները կվերակայվեն 10 վայրկյան հետո:"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Մարտկոց (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Մարտկոցի տնտեսումը լիցքավորման ժամանակ հասանելի չէ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Մարտկոցի տնտեսում"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Նվազեցնում է ծանրաբեռնվածությունը և ֆոնային տվյալները"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 9075484..f7f3370 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Tunjukkan notifikasi ini tanpa suara"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Tunjukkan di bagian atas daftar notifikasi dan bunyikan suara"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Muncul di layar dan bunyikan suara"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Setelan lainnya"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Warna normal"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Warna malam"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Warna khusus"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Terapkan"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Konfirmasi setelan"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Beberapa setelan warna dapat membuat perangkat ini tidak dapat digunakan. Klik OKE untuk mengonfirmasi setelan warna ini. Jika tidak, setelan ini akan disetel ulang setelah 10 detik."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Baterai (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penghemat Baterai tidak tersedia selama pengisian daya"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Penghemat Baterai"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Mengurangi performa dan data latar belakang"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 556018c..c1d3591 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Sýna þessar tilkynningar án hljóðs"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Sýna efst á tilkynningalistanum og spila hljóð"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Birta á skjánum og spila hljóð"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Fleiri stillingar"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Lokið"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Venjulegir litir"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Næturlitir"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Sérsniðnir litir"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Nota"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Staðfesta stillingar"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Sumar litastillingar kunna að bitna á notagildi tækisins. Veldu „Í lagi“ til að staðfesta þessar litastillingar, að öðrum kosti verða litirnir endurstilltir eftir tíu sekúndur."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Rafhlaða (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ekki er hægt að nota rafhlöðusparnað meðan á hleðslu stendur"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Rafhlöðusparnaður"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Dregur úr afköstum og bakgrunnsgögnum"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index b49c387..dd65970 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostra silenziosamente queste notifiche"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostra nella parte superiore dell\'elenco delle notifiche e riproduci suono"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Apri sullo schermo e riproduci suono"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Fine"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Colori normali"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Colori per la notte"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Colori personalizzati"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Applica"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Conferma le impostazioni"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Alcune impostazioni relative ai colori potrebbero rendere inutilizzabile il dispositivo. Fai clic su OK per confermare queste impostazioni; in caso contrario, le impostazioni verranno reimpostate dopo 10 secondi."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batteria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Risparmio energetico non disponibile durante la ricarica"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Risparmio energetico"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Riduce le prestazioni e i dati in background"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index e6ac5af..95bbe20 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"הצג את ההודעות האלה בלי להשמיע צליל"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"הצג בחלק העליון של רשימת ההודעות והשמע צליל"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"הצג לרגע על המסך והשמע צליל"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"הגדרות נוספות"</string>
+    <string name="notification_done" msgid="5279426047273930175">"סיום"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"צבעים רגילים"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"צבעי לילה"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"צבעים מותאמים אישית"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"החל"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"אישור הגדרות"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"הגדרות צבע מסוימות עלולות להפוך את המכשיר הזה לבלתי שמיש. לחץ על אישור כדי לאשר את הגדרות הצבע האלה, אחרת הגדרות אלה יתאפסו לאחר 10 שניות."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"סוללה (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"תכונת החיסכון בסוללה אינה זמינה בעת טעינת המכשיר"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"חיסכון בסוללה"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"מפחית את רמת הביצועים ואת נתוני הרקע"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index e7e3a86..ed0c7e9 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"この通知をマナーモードで表示する"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"通知リストの先頭に表示し、音声でも知らせる"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"画面に数秒間表示し、音声でも知らせる"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"詳細設定"</string>
+    <string name="notification_done" msgid="5279426047273930175">"完了"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"標準の色"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"夜間の色"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"カスタムの色"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"適用"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"設定の確認"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"一部の色設定を適用すると、この端末を使用できなくなることがあります。この色設定を確認するには、[OK] をクリックしてください。確認しない場合、10 秒後に設定はリセットされます。"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"バッテリー(<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電中はバッテリー セーバーは利用できません"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"バッテリー セーバー"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"パフォーマンスとバックグラウンド データを制限します"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 79e0f4f..5c861f8 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ამ შეტყობინებების უხმოდ ჩვენება"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"ამ შეტყობინებების სიის თავში, ხმოვან სიგნალთან ერთად ჩვენება"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"ამ შეტყობინებების პირდაპირ ეკრანზე, ხმოვან სიგნალთან ერთად ჩვენება"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"დამატებითი პარამეტრები"</string>
+    <string name="notification_done" msgid="5279426047273930175">"მზადაა"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ჩვეულებრივი ფერები"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ღამის ფერები"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"მორგებული ფერები"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"გამოყენება"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"პარამეტრების დადასტურება"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"ფერთა ზოგიერთ პარამეტრს ამ მოწყობილობასთან მუშაობის გართულება შეუძლია. ფერთა ამჟამინდელი პარამეტრების დასადასტურებლად, დააწკაპუნეთ „კარგი“-ზე. წინააღმდეგ შემთხვევაში, პარამეტრები 10 წამის შემდეგ ჩამოიყრება."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ბატარეა (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ბატარეის დამზოგი დატენვისას მიწვდომელია"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ბატარეის დამზოგი"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ამცირებს წარმადობას და ფონურ მონაცემებს"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 5187b54..42e6a5d 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Осы хабарландыруларды үнсіз көрсету"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Хабарландыруларды тізімінің жоғарғы жағында көрсету және дыбыс шығару"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Экранға бекіту және дыбыс шығару"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Қосымша параметрлер"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Дайын"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Қалыпты түстер"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Түнгі түстер"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Арнаулы түстер"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Қолдану"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Параметрлерді растау"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Кейбір түс параметрлері бұл құрылғыны пайдалану мүмкін емес етуі мүмкін. Бұл түс параметрлерін растау үшін OK түймесін басыңыз, әйтпесе параметрлер 10 секундтан кейін ысырылады."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Батарея (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Батарея үнемдегіш"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Өнімділікті және фондық деректерді азайтады"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 5665a90..53f5868 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"បង្ហាញការជូនដំណឹងទាំងនេះស្ងាត់ៗ"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"បង្ហាញនៅផ្នែកខាងលើបញ្ជីនៃការជូនដំណឹង និងបន្លឺសំឡេង"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"លោតបង្ហាញនៅលើអេក្រង់ និងបន្លឺសំឡេង"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"ការកំណត់ច្រើនទៀត"</string>
+    <string name="notification_done" msgid="5279426047273930175">"រួចរាល់"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ពណ៌ធម្មតា"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ពណ៌ពេលយប់"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ពណ៌ផ្ទាល់ខ្លួន"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"អនុវត្ត"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"បញ្ជាក់ការកំណត់"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"ការកំណត់ពណ៌មួយចំនួនអាចធ្វើឲ្យឧបករណ៍នេះមិនអាចប្រើបាន។ សូមចុច យល់ព្រម ដើម្បីបញ្ជាក់ការកំណត់ពណ៌ទាំងនេះ បើមិនដូច្នេះទេការកំណត់ទាំងនេះនឹងកំណត់ឡើងវិញក្នុងរយៈពេល 10 វិនាទីបន្ទាប់។"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ថ្ម (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"កម្មវិធីសន្សំថ្ម"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"កាត់បន្ថយប្រតិបត្តិការ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 0326a67..1b212f0 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಸ್ಥಬ್ಧವಾಗಿ ತೋರಿಸು"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"ಅಧಿಸೂಚನೆಗಳ ಪಟ್ಟಿಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ ತೋರಿಸು ಮತ್ತು ಧ್ವನಿ ಮಾಡು"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"ಪರದೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಧ್ವನಿ ಮಾಡು"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ಸಾಮಾನ್ಯ ಬಣ್ಣಗಳು"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ರಾತ್ರಿ ಬಣ್ಣಗಳು"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ಕಸ್ಟಮ್ ಬಣ್ಣಗಳು"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"ಅನ್ವಯಿಸು"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಖಚಿತಪಡಿಸಿ"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"ಕೆಲವು ಬಣ್ಣ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಈ ಸಾಧನವನ್ನು ಅನುಪಯುಕ್ತಗೊಳಿಸಬಹುದು. ಈ ಬಣ್ಣ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಖಚಿತಪಡಿಸಲು ಸರಿ ಕ್ಲಿಕ್ ಮಾಡಿ, ಇಲ್ಲವಾದರೆ ಈ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು 10 ಸೆಕೆಂಡುಗಳ ನಂತರ ಮರುಹೊಂದಿಸಿ."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ಬ್ಯಾಟರಿ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ಚಾರ್ಜಿಂಗ್ ಸಮಯದಲ್ಲಿ ಬ್ಯಾಟರಿ ಸೇವರ್‌‌ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ಬ್ಯಾಟರಿ ಸೇವರ್‌‌"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ಕಾರ್ಯಕ್ಷಮತೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಡೇಟಾವನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b910c04..ecff2ad 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"무음으로 알림 표시"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"알림 목록 상단에 표시하고 소리로 알림"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"화면에 표시하고 소리로 알림"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"설정 더보기"</string>
+    <string name="notification_done" msgid="5279426047273930175">"완료"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"일반 색상"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"야간 색상"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"맞춤 색상"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"적용"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"설정 확인"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"일부 색상 설정으로 인해 이 기기를 사용하지 못할 수 있습니다. 확인을 클릭하여 이러한 색상 설정을 확인하지 않으면 10초 후에 설정이 초기화됩니다."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"배터리(<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"충전하는 동안 배터리 세이버는 사용할 수 없습니다."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"배터리 세이버"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"성능 및 백그라운드 데이터를 줄입니다."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index e703e00..ad430ec 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Бул эскертмелер үнсүз көрсөтүлсүн"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Эскертмелер тизмесинин эң жогорусунда үн чыгарып көрсөтүлсүн"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Үн менен коштолуп, экранга чыгарылсын"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Аткарылды"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Кадимки түстөр"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Түнкү түстөр"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Ыңгайлаштырылган түстөр"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Колдонуу"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Жөндөөлөрдү ырастоо"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Айрым түс жөндөөлөрү бул түзмөктү колдонулгус кылып коюшу мүмкүн. Бул түс жөндөөлөрүн ырастоо үчүн OK баскычын чыкылдатыңыз, болбосо бул жөндөөлөр 10 секунддан кийин баштапкы абалына келтирилет."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Батарея (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Батареяны үнөмдөгүч кубаттоо учурунда иштебейт"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Батареяны үнөмдөгүч"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Иштин майнаптуулугун начарлатып, фондук дайындарды чектейт"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 8e43408..48d94cd 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ສະແດງການແຈ້ງເຕືອນເຫຼົ່ານີ້ແບບບໍ່ມີສຽງ"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"ສະແດງຢູ່ສ່ວນເທິງຂອງລາຍການແຈ້ງເຕືອນ ແລະສົ່ງສຽງດັງ"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"ເດັ້ງຂຶ້ນເທິງຫນ້າຈໍ ແລະສົ່ງສຽງດັງ"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"​ການ​ຕັ້ງ​ຄ່າ​ເພີ່ມ​ເຕີມ"</string>
+    <string name="notification_done" msgid="5279426047273930175">"ສຳເລັດແລ້ວ"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ສີ​ປົກ​ກະ​ຕິ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ສີ​ຕອນ​ກາງ​ຄືນ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ສີແບບກຳນົດເອງ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"ນຳໃຊ້"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ຢືນ​ຢັນ​ການ​ຕັ້ງ​ຄ່າ"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"ບາງການຕັ້ງຄ່າສີສາມາດເຮັດໃຫ້ອຸປະກອນນີ້ບໍ່ສາມາດໃຊ້ໄດ້. ຄລິກ ຕົກລົງ ເພື່ອຢືນຢັນການຕັ້ງຄ່າສີເຫຼົ່ານີ້, ຖ້າບໍ່ດັ່ງນັ້ນ ການຕັ້ງຄ່າເຫຼົ່ານີ້ຈະຕັ້ງຄືນໃໝ່ ຫຼັງຈາກ 10 ວິນາທີ."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ແບັດເຕີຣີ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ຕົວປະຢັດແບັດເຕີຣີບໍ່ມີໃຫ້ນຳໃຊ້ໃນລະຫວ່າງການສາກ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ຕົວປະຢັດ​ແບັດເຕີຣີ"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ຫຼຸດ​ປະ​ສິ​ທິ​ພາບ​ການໃຊ້ງານ ແລະ ​ຂໍ້​ມູນ​ພື້ນຫຼັງ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 04066a7..adb5a53 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Tyliai rodyti šiuos pranešimus"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Rodyti pranešimų sąrašo viršuje ir skambėti"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Rodyti ekrane ir skambėti"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Atlikta"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Įprastos spalvos"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nakties spalvos"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Tinkintos spalvos"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Taikyti"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Nustatymų patvirtinimas"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Dėl kai kurių spalvų nustatymų įrenginys gali būti netinkamas naudoti. Spustelėkite „Gerai“, kad patvirtintumėte šiuos spalvų nustatymus. Kitaip šie nustatymai bus nustatyti iš naujo po 10 sekundžių."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Akumuliatorius (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumuliatoriaus tausojimo priemonė"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Sumažinamas našumas ir foninių duomenų naudojimas"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 649d07b..b8a6d01 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -461,6 +461,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Rādīt šos paziņojumus bez skaņas signāla"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Rādīt paziņojumu saraksta augšdaļā un ar skaņas signālu"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Rādīt ekrānā ar skaņas signālu"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Citi iestatījumi"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Gatavs"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Parastas krāsas"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nakts krāsas"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Pielāgotas krāsas"</string>
@@ -471,4 +473,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Lietot"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Iestatījumu apstiprināšana"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Noteiktu krāsu iestatījumu dēļ šī ierīce var kļūt nelietojama. Lai apstiprinātu šos krāsu iestatījumus, noklikšķiniet uz Labi. Ja to neizdarīsiet, pēc 10 sekundēm šie iestatījumi tiks atiestatīti."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Akumulators (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumulatora jaudas taupīšanas režīms uzlādes laikā nav pieejams."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumulatora jaudas taupīšanas režīms"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Samazina veiktspēju un fona datus."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 928bbcd..8ba8094 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Тивко прикажувај ги известувањава"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Прикажувај ги на врвот на списокот со известувања и дај звучен сигнал"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Појави се на екранот и дај звучен сигнал"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Повеќе поставки"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Нормални бои"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Ноќни бои"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Приспособени бои"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Примени"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Потврдете ги поставките"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Некои поставки на боите може да го направат уредот неупотреблив. Кликнете на Во ред за да ги потврдите овие поставки на боите, инаку тие поставки ќе се ресетираат по 10 секунди."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Батерија (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Штедачот на батерија не е достапен при полнење"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Штедач на батерија"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Ја намалува изведбата и податоците во заднина"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 2f4b81a..240373b 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ഈ അറിയിപ്പുകൾ നിശബ്ദമായി കാണിക്കുക"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"അറിയിപ്പ് ലിസ്റ്റിന്റെ ഏറ്റവും മുകളിൽ കാണിക്കുക, ശബ്ദമുണ്ടാക്കുക"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"സ്ക്രീനിൽ ദൃശ്യമാക്കുക, ശബ്ദമുണ്ടാക്കുക"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string>
+    <string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"സാധാരണ വര്‍ണ്ണങ്ങൾ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"രാത്രി വര്‍ണ്ണങ്ങൾ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ഇഷ്ടാനുസൃത വര്‍ണ്ണങ്ങൾ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"ബാധകമാക്കുക"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ക്രമീകരണം സ്ഥിരീകരിക്കുക"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"ചില വർണ്ണ ക്രമീകരണത്തിന് ഈ ഉപകരണത്തെ ഉപയോഗരഹിതമാക്കാനാകും. ഈ വർണ്ണ ക്രമീകരണം സ്ഥിരീകരിക്കുന്നതിന് ശരി എന്നതിൽ ക്ലിക്കുചെയ്യുക, അല്ലെങ്കിൽ 10 സെക്കൻഡിന് ശേഷം ഈ ക്രമീകരണം പുനഃക്രമീകരിക്കപ്പെടും."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ബാറ്ററി (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ചാർജുചെയ്യുന്ന സമയത്ത് ബാറ്ററി സേവർ ലഭ്യമല്ല"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ബാറ്ററി സേവർ"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്‌ക്കുന്നു"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 3dd084e..de5c035 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -458,6 +458,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Эдгээр мэдэгдлийг дуугүй харуулах"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Мэдэгдлийг жагсаалтын эхэнд дуутай харуулах"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Дэлгэцэнд яаралтайгаар дуутай гаргах"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Бусад тохиргоо"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Дууссан"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Хэвийн өнгө"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Шөнийн өнгө"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Өгөгдмөл өнгө"</string>
@@ -468,4 +470,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Хэрэгжүүлэх"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Тохиргоог баталгаажуулах"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Зарим өнгөний тохиргоо энэ төхөөрөмжийг ашиглах боломжгүй болгож болзошгүй. OK товчлуурыг дарж эдгээр өнгөний тохиргоог зөвшөөрөхгүй бол энэ тохиргоо нь 10 секундын дараа шинэчлэгдэх болно."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Тэжээл (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Цэнэглэх үед тэжээл хэмнэгч ажиллахгүй"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Тэжээл хэмнэгч"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Гүйцэтгэл болон дэвсгэрийн датаг багасгадаг"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 59eadf1..80dc12e 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"या सूचना शांतपणे दर्शवा"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"सूचना सूचीच्या शीर्षस्थानी दर्शवा आणि ध्वनी चालू करा"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"स्क्रीनवर डोकावून पहा आणि ध्वनी चालू करा"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string>
+    <string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"सामान्य रंग"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"रात्रीचे रंग"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"सानुकूल रंग"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"लागू करा"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"सेटिंग्जची पुष्टी करा"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"काही रंग सेटिंग्ज या डिव्हाइसला निरुपयोगी करू शकतात. या रंग सेटिंग्जची पुष्टी करण्‍यासाठी ठीक आहे दाबा अन्यथा या सेटिंग्ज 10 सेकंदांनंतर रीसेट होतील."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"बॅटरी (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज करताना बॅटरी बचतकर्ता उपलब्ध नाही"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"बॅटरी बचतकर्ता"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"कार्यप्रदर्शन आणि पार्श्वभूमी डेटा कमी करते"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 5dd221a..ec4b28c 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Tunjukkan pemberitahuan ini secara senyap"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Tunjukkan pada bahagian atas senarai pemberitahuan dan bunyikan"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Intai pada skrin dan bunyikan"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Lagi tetapan"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Warna biasa"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Warna malam"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Warna tersuai"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Gunakan"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Sahkan tetapan"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Sesetengah tetapan warna boleh menjadikan peranti ini tidak dapat digunakan. Klik OK untuk mengesahkan tetapan warna ini, jika tidak, tetapan ini akan ditetapkan semula selepas 10 saat."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateri (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penjimat Bateri tidak tersedia semasa mengecas"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Penjimat Bateri"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Mengurangkan prestasi dan data latar belakang"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 293e010..257dd43 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ဤသတိပေးချက်များကို တိတ်တဆိတ်ပြပါ"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"သတိပေးချက်စာရင်းများ၏ ထိပ်တွင်ပြကာ အသံဖွင့်ပါ"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"မျက်နှာပြင်ပေါ်သို့ ဖော်ပြကာ အသံဖွင့်ပါ"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"နောက်ထပ် ဆက်တင်များ"</string>
+    <string name="notification_done" msgid="5279426047273930175">"ပြီးပါပြီ"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ပုံမှန် အရောင်များ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ည အရောင်များ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"စိတ်ကြိုက် အရောင်များ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"အသုံးပြုပါ"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ဆက်တင်များကို အတည်ပြုပါ"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"အချို့သော အရောင်ဆက်တက်များက ဤကိရိယာကို သုံးမရအောင် လုပ်ပစ်နိုင်ပါသည်။ ဤအရောင် ဆက်တင်များကို အတည်ပြုရန် အိုကေကို နှိပ်ပါ၊ သို့မဟုတ် ဤဆက်တင်များကို ၁၀ စက္ကန့် အကြာတွင် ပြန်ညှိလိုက်ပါမည်။"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ဘက်ထရီ ( <xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"အားသွင်းနေချိန်မှာ Battery Saver ကို သုံးမရပါ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"လုပ်ဆောင်မှု နှင့် နောက်ခံ ​ဒေတာကို လျော့နည်းစေပါသည်"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ffc2fcf..4446a77 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Vis disse varslene uten lyd"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Vis øverst på varsellisten med lyd"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Vis fort på skjermen med lyd"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Flere innstillinger"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Ferdig"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normale farger"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nattfarger"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Spesialtilpassede farger"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Bruk"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Bekreft innstillingene"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Noen fargeinnstillinger kan gjøre denne enheten ubrukelig. Klikk på OK for å bekrefte disse fargeinnstillingene, ellers blir de tilbakestilt etter ti sekunder."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batteri (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparing er ikke tilgjengelig under lading"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparing"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduserer ytelsen og begrenser bakgrunnsdataene"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 6f40d55..f1009550 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"यी सूचनाहरू बिना आवाज देखाउनुहोस्"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"सूचना सूचीको शीर्षमा देखाउनुहोस् र आवाज निकाल्नुहोस्"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"स्क्रिनमा हेर्नुहोस् र आवाज निकाल्नुहोस्"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string>
+    <string name="notification_done" msgid="5279426047273930175">"सम्पन्‍न भयो"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"सामान्य रङहरू"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"रात्री रङहरू"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"अनुकूलन रङहरू"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"लागू गर्नुहोस्"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"सेटिङहरूको पुष्टि गर्नुहोस्"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"केही रङ सेटिङहरूले यस यन्त्रलाई अनुपयोगी बनाउन सक्छन्। यी रङ सेटिङहरू पुष्टि गर्न ठीक छ मा क्लिक गर्नुहोस्, अन्यथा यी सेटिङहरू १० सेकेण्डपछि रिसेट हुनेछन्।"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ब्याट्री (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज गर्ने समयमा ब्याट्री सेभर उपलब्ध छैन"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ब्याट्री सेभर"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"कार्यसम्पादन र पृष्ठभूमि डेटा घटाउँछ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 27eb1d11..c6562d6 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Deze meldingen zonder geluid weergeven"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Boven aan de lijst met meldingen weergeven en geluid laten horen"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Op het scherm weergeven en geluid laten horen"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Meer instellingen"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Gereed"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normale kleuren"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nachtkleuren"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Aangepaste kleuren"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Toepassen"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Instellingen bevestigen"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Bij sommige kleurinstellingen kan het apparaat onbruikbaar worden. Klik op OK om deze kleurinstellingen te bevestigen, anders worden deze instellingen na tien seconden gereset."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Accu (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Accubesparing niet beschikbaar tijdens opladen"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Accubesparing"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vermindert de prestaties en achtergrondgegevens"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 2dd91ec..fd17dac 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਚੁੱਪਚਾਪ ਢੰਗ ਨਾਲ ਵਿਖਾਓ"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"ਸੂਚਨਾਵਾਂ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਵਿਖਾਓ ਅਤੇ ਆਵਾਜ਼ ਕਰੋ"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"ਸਕਰੀਨ \'ਤੇ ਝਾਤੀ ਮਾਰੋ ਅਤੇ ਆਵਾਜ਼ ਕਰੋ"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
+    <string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"ਸਧਾਰਨ ਰੰਗ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"ਰਾਤ ਦੇ ਰੰਗ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"ਕਸਟਮ ਰੰਗ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"ਲਾਗੂ ਕਰੋ"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ਸੈਟਿੰਗਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"ਕੁਝ ਰੰਗ ਸੈਟਿੰਗਾਂ ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਬੇਕਾਰ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਇਹਨਾਂ ਰੰਗ ਸੈਟਿੰਗਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਠੀਕ \'ਤੇ ਕਲਿੱਕ ਕਰੋ, ਨਹੀਂ ਤਾਂ ਇਹ ਸੈਟਿੰਗਾਂ 10 ਸਕਿੰਟ ਬਾਅਦ ਮੁੜ-ਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ।"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"ਬੈਟਰੀ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਰਜਿੰਗ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ਬੈਟਰੀ ਸੇਵਰ"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ਕਾਰਗੁਜ਼ਾਰੀ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਡੈਟੇ ਨੂੰ ਘਟਾਉਂਦਾ ਹੈ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index bf222bc..3630429 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Pokazuj te powiadomienia bez sygnału dźwiękowego"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Pokazuj na górze listy powiadomień i sygnalizuj dźwiękiem"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Wyświetlaj na ekranie i odtwarzaj dźwięk"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Więcej ustawień"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Gotowe"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Kolory standardowe"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Kolory nocne"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Kolory niestandardowe"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Zastosuj"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Potwierdź ustawienia"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Niektóre ustawienia kolorów mogą utrudniać korzystanie z urządzenia. Kliknij OK, by potwierdzić te ustawienia kolorów. Jeśli tego nie zrobisz, zostaną one zresetowane po 10 sekundach."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Oszczędzanie baterii nie jest dostępne podczas ładowania"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Oszczędzanie baterii"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Zmniejsza wydajność i ogranicza dane w tle"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 5d43efa..7fa28ad 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar essas notificações de forma silenciosa"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificações e emitir som"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar parcialmente na tela e emitir som"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Cores noturnas"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configurações"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algumas configurações de cor podem tornar o dispositivo inutilizável. Clique em \"OK\" para confirmar essas configurações de cor; caso contrário, essas configurações serão redefinidas após 10 segundos."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados em segundo plano"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 44ecd41..3d29d7e 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -425,7 +425,7 @@
     <string name="status_bar_airplane" msgid="7057575501472249002">"Modo de avião"</string>
     <string name="add_tile" msgid="2995389510240786221">"Adicionar mosaico"</string>
     <string name="broadcast_tile" msgid="3894036511763289383">"Mosaico de transmissão"</string>
-    <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Só vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g> se desativar esta funcionalidade antes dessa hora"</string>
+    <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Só vai ouvir o próximo alarme (<xliff:g id="WHEN">%1$s</xliff:g>) se desativar esta funcionalidade antes dessa hora"</string>
     <string name="zen_alarm_warning" msgid="444533119582244293">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="3980063409350522735">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="4242179982586714810">"em <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar estas notificações sem som"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificações e emitir som"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar no ecrã e emitir som"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Cores noturnas"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmar as definições"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algumas definições de cor podem tornar este dispositivo instável. Clique em OK para confirmar estas definições de cor. Caso contrário, estas definições serão repostas após 10 segundos."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Poupança de bateria não disponível durante o carregamento"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Poupança de bateria"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados de segundo plano"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 5d43efa..7fa28ad 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Mostrar essas notificações de forma silenciosa"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Mostrar na parte superior da lista de notificações e emitir som"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Mostrar parcialmente na tela e emitir som"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Cores normais"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Cores noturnas"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Cores personalizadas"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmar configurações"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Algumas configurações de cor podem tornar o dispositivo inutilizável. Clique em \"OK\" para confirmar essas configurações de cor; caso contrário, essas configurações serão redefinidas após 10 segundos."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados em segundo plano"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e9e7bdc..13f3ef2 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -461,6 +461,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Aceste notificări se afișează fără a se emite un sunet"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Se afișează în partea de sus a listei cu notificări și se emite un sunet"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Se afișează pentru o scurtă durată pe ecran și se emite un sunet"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Terminat"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Culori normale"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Culori de noapte"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Culori personalizate"</string>
@@ -471,4 +473,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Aplicați"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Confirmați setările"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Unele setări pentru culori pot face dispozitivul să nu mai funcționeze. Dați clic pe OK pentru a confirma aceste setări pentru culori. În caz contrar, acestea se vor reseta după 10 secunde."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Baterie (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Economisirea bateriei nu este disponibilă pe durata încărcării"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Economisirea bateriei"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce performanța și datele de fundal"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index b68b434..4d7ee69 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Показывать уведомления без звука."</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Показывать со звуком в начале списка уведомлений."</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Показывать со звуком поверх всех окон."</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Обычные цвета"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Ночные цвета"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Собственные цвета"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Применить"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Подтвердите настройки"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Некоторые цветовые настройки могут затруднить работу с устройством. Чтобы применить выбранные параметры, нажмите \"ОК\". В противном случае они будут сброшены через 10 секунд."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Батарея (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим энергосбережения нельзя включить во время зарядки"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим энергосбережения"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Замедляет работу устройства и ограничивает фоновую передачу данных"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index b9f7e53..f73ff6c 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"නිශ්ශබ්දව මෙම දැනුම්දීම් පෙන්වන්න"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"දැනුම්දීම් ලැයිස්තුවෙහි ඉහළින්ම පෙන්වන්න සහ ශබ්ද කරන්න"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"තිරයට පැමිණ ශබ්ද කරන්න"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"තව සැකසීම්"</string>
+    <string name="notification_done" msgid="5279426047273930175">"නිමයි"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"සාමාන්‍ය වර්ණ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"රාත්‍රී වර්ණ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"අභිරුචි වර්ණ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"යොදන්න"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"සැකසීම් තහවුරු කරන්න"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"සමහර වර්ණ සැකසීම් මෙම උපාංගය භාවිත කළ නොහැකි තත්ත්වයට පත් කළ හැකිය. මෙම වර්ණ සැකසීම් තහවුරු කිරීමට හරි ක්ලික් කරන්න, නැතහොත් මෙම සැකසීම් තත්පර 10කට පසුව යළි සකසනු ඇත."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"බැටරිය (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ආරෝපණය අතරතුර බැටරි සුරැකුම ලබා ගත නොහැකිය."</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"බැටරි සුරැකුම"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ක්‍රියාකාරිත්වය සහ පසුබිම් දත්ත අඩු කරන්න"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 91a143e..ddf2de8 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Tieto upozornenia zobrazovať bez zvukového signálu"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Zobrazovať v hornej časti zoznamu upozornení so zvukovým signálom"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Zobrazovať cez obrazovku so zvukovým signálom"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Ďalšie nastavenia"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normálne farby"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nočné farby"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Vlastné farby"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Použiť"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Potvrdenie nastavení"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Niektoré nastavenia farieb môžu toto zariadenie znefunkčniť. Tieto nastavenia farieb potvrdíte kliknutím na tlačidlo OK, ináč sa tieto nastavenia o 10 sekúnd obnovia."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batéria (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Počas nabíjania nie je Šetrič batérie k dispozícii"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Šetrič batérie"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Obmedzí výkonnosť a údaje na pozadí"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 96974ae..249c1ad 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Prikaži ta obvestila brez zvoka"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Prikaži na vrhu seznama obvestil in predvajaj zvok"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Za hip pokaži predogled na zaslonu in predvajaj zvok"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Dokončano"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Običajne barve"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nočne barve"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Barve po meri"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Uporabi"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Potrditev nastavitev"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Zaradi nekaterih barvnih nastavitev lahko postane ta naprava neuporabna. Kliknite »V redu«, če želite potrditi te barvne nastavitve. V nasprotnem primeru se bodo čez 10 sekund ponastavile na prvotno vrednost."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Akumulator (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Varčevanje z energijo akumulatorja med polnjenjem ni na voljo"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Varčevanje z energijo akumulatorja"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Omeji zmogljivost delovanja in prenos podatkov v ozadju"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index b904ae1..60e2e80 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Shfaqi këto njoftime në heshtje"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Shfaqi në krye të listës së njoftimeve dhe lësho tingull"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Shfaq një vështrim të shpejtë në ekran dhe lësho tingull"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Cilësime të tjera"</string>
+    <string name="notification_done" msgid="5279426047273930175">"U krye"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Ngjyrat normale"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Ngjyrat e natës"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Ngjyrat e personalizuara"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Zbato"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Konfirmo cilësimet"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Disa cilësime ngjyrash mund ta bëjnë këtë pajisje të papërdorshme. Kliko OK për të konfirmuar këto cilësime ngjyrash, përndryshe këto cilësime do të rivendosen pas 10 sekondash."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"\"Kursyesi i baterisë\" nuk është i disponueshëm gjatë karikimit"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Kursyesi i baterisë"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Pakëson veprimtarinë dhe të dhënat në sfond"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 6470fcc..4954f05 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -455,12 +455,14 @@
     <string name="low_importance" msgid="4109929986107147930">"Мала важност"</string>
     <string name="default_importance" msgid="8192107689995742653">"Уобичајена важност"</string>
     <string name="high_importance" msgid="1527066195614050263">"Велика важност"</string>
-    <string name="max_importance" msgid="5089005872719563894">"Највећа важност"</string>
+    <string name="max_importance" msgid="5089005872719563894">"Важност: хитно"</string>
     <string name="notification_importance_blocked" msgid="2397192642657872872">"Ова обавештења се никада не приказују"</string>
     <string name="notification_importance_low" msgid="4383563267370859725">"Приказују се у дну листе обавештења без звука"</string>
     <string name="notification_importance_default" msgid="4926529615920610817">"Ова обавештења се приказују без звука"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Приказују се у врху листе обавештења и емитује се звук"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Накратко се приказују на екрану и емитује се звук"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Још подешавања"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Нормалне боје"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Ноћне боје"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Прилагођене боје"</string>
@@ -471,4 +473,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Примени"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Потврдите подешавања"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Нека подешавања боја могу да учине уређај неупотребљивим. Кликните на Потврди да бисте потврдили ова подешавања боја, пошто ће се у супротном ова подешавања ресетовати након 10 секунди."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Батерија (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Уштеда батерије није доступна током пуњења"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Уштеда батерије"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Смањује перформансе и позадинске податке"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 5f2b397..8f67f0e 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Visa aviseringarna utan ljud"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Visa högst upp på listan, med ljud"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Visa på skärmen, med ljud"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Fler inställningar"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Klar"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normala färger"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Nattfärger"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Anpassade färger"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Verkställ"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Bekräfta inställningarna"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Vissa färginställningar kan göra den här enheten oanvändbar. Klicka på OK om du vill bekräfta färginställningarna, annars återställs inställningarna efter 10 sekunder."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Batteri (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparläget är inte tillgängligt vid laddning"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparläge"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Minskar prestanda och bakgrundsdata"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 8f962da..0f66826 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Onyesha arifa hizi bila sauti"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Onyesha katika sehemu ya juu ya orodha ya arifa na itoe sauti"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Weka onyesho la kuchungulia kwenye skrini na itoe sauti"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Rangi za kawaida"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Rangi za usiku"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Rangi maalum"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Tumia"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Thibitisha mipangilio"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Baadhi ya mipangilio ya rangi inaweza kufanya kifaa hiki kisitumike. Bofya Sawa ili uthibitishe mipangilio hii ya rangi, vinginevyo, mipangilio hii itajiweka upya baada ya sekunde 10."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Betri (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Kiokoa Betri hakipatikani unapochaji betri"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Kiokoa Betri"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Hupunguza data ya chini chini na utendaji"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index a625c6f..0653843 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ஒலியின்றி இந்த அறிவிப்புகளைக் காட்டு"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"அறிவிப்புகள் பட்டியலின் மேல் பகுதியில் ஒலியுடன் காட்டு"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"ஒலியுடன் திரையில் காட்டு"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"மேலும் அமைப்புகள்"</string>
+    <string name="notification_done" msgid="5279426047273930175">"முடிந்தது"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"இயல்பான வண்ணங்கள்"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"இரவுநேர வண்ணங்கள்"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"தனிப்பயன் வண்ணங்கள்"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"பயன்படுத்து"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"அமைப்புகளை உறுதிப்படுத்து"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"சில வண்ண அமைப்புகள் இந்தச் சாதனத்தைப் பயன்படுத்த முடியாதபடி செய்யலாம். இந்த வண்ண அமைப்புகளை உறுதிப்படுத்த, சரி என்பதைக் கிளிக் செய்யவும், இல்லையெனில் இந்த அமைப்புகள் 10 வினாடிகளுக்குப் பின் மீட்டமைக்கப்படும்."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"பேட்டரி (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"சார்ஜ் செய்யும் போது பேட்டரி சேமிப்பானைப் பயன்படுத்த முடியாது"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"பேட்டரி சேமிப்பான்"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"செயல்திறனையும் பின்புலத்தில் தரவு செயலாக்கப்படுவதையும் குறைக்கும்"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 9d1403f..f711544 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"ఈ నోటిఫికేషన్‌లను శబ్దం లేకుండా చూపుతుంది"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"నోటిఫికేషన్‌ల జాబితా ఎగువ భాగంలో శబ్దంతో చూపుతుంది"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"స్క్రీన్‌పై శీఘ్రంగా శబ్దంతో చూపుతుంది"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"మరిన్ని సెట్టింగ్‌లు"</string>
+    <string name="notification_done" msgid="5279426047273930175">"పూర్తయింది"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"సాధారణ రంగులు"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"రాత్రి రంగులు"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"అనుకూల రంగులు"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"వర్తింపజేయి"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"సెట్టింగ్‌లను నిర్ధారించండి"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"కొన్ని రంగు సెట్టింగ్‌ల వలన ఈ పరికరం ఉపయోగించలేని విధంగా అయిపోవచ్చు. ఈ రంగు సెట్టింగ్‌లను నిర్ధారించడానికి సరే క్లిక్ చేయండి లేదంటే ఈ సెట్టింగ్‌లు 10 సెకన్ల తర్వాత రీసెట్ చేయబడతాయి."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"బ్యాటరీ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ఛార్జ్ అవుతున్న సమయంలో బ్యాటరీ సేవర్ అందుబాటులో ఉండదు"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"బ్యాటరీ సేవర్"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 38e028c..8c284a1 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"แสดงการแจ้งเตือนเหล่านี้โดยไม่ส่งเสียง"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"แสดงที่ด้านบนของรายการแจ้งเตือนและส่งเสียง"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"แสดงบนหน้าจอในช่วงเวลาสั้นๆ และส่งเสียง"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"การตั้งค่าเพิ่มเติม"</string>
+    <string name="notification_done" msgid="5279426047273930175">"เสร็จสิ้น"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"สีปกติ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"สียามค่ำคืน"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"สีที่กำหนดเอง"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"ใช้"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ยืนยันการตั้งค่า"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"การตั้งค่าสีบางอย่างอาจทำให้อุปกรณ์นี้ใช้งานไม่ได้ คลิกตกลงเพื่อยืนยันการตั้งค่าสีเหล่านี้ มิฉะนั้นระบบจะรีเซ็ตการตั้งค่าหลังจาก 10 วินาที"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"แบตเตอรี่ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ไม่สามารถใช้โหมดประหยัดแบตเตอรี่ระหว่างการชาร์จ"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"โหมดประหยัดแบตเตอรี่"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ลดประสิทธิภาพการทำงานและข้อมูลแบ็กกราวด์"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 5815041..33e5968 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Tahimik na ipakita ang mga notification na ito"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Ipakita sa itaas ng listahan ng mga notification at mag-play ng tunog"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Ipasilip sa screen at mag-play ng tunog"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Higit pang mga setting"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Tapos Na"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Mga karaniwang kulay"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Madidilim na kulay"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Mga custom na kulay"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Ilapat"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Kumpirmahin ang mga setting"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Maaaring hindi magamit ang device na ito dahil sa ilang setting ng kulay. I-click ang OK upang kumpirmahin ang mga setting ng kulay na ito, kung hindi ay mare-reset ang mga setting na ito pagkatapos ng 10 segundo."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Baterya (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Hindi available ang Pangtipid sa Baterya kapag nagcha-charge"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Pangtipid sa Baterya"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Binabawasan ang pagganap at data sa background"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6832808..d937dc3 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Bu bildirimleri sessizce göster"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Bildirim listesinin en üstünde göster ve ses çıkar"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Ekrana getir ve ses çıkar"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Diğer ayarlar"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Bitti"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Normal renkler"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Gece renkleri"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Özel renkler"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Uygula"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Ayarları onaylayın"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Bazı renkler bu cihazı kullanılmaz yapabilir. Bu renkleri onaylamak için Tamam\'ı tıklayın. Tıklamazsanız bu ayarlar 10 saniye sonra sıfırlanacaktır."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Pil (%%<xliff:g id="ID_1">%1$d</xliff:g>)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Şarj sırasında Pil Tasarrufu özelliği kullanılamaz"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Pil Tasarrufu"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Performansı ve arka plan verilerini azaltır"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 9736028..5503cb8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -462,6 +462,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Показувати ці сповіщення без звукового сигналу"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Показувати сповіщення вгорі списку зі звуковим сигналом"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Показувати сповіщення на екрані зі звуковим сигналом"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Більше налаштувань"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Готово"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Стандартні кольори"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Нічні кольори"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Користувацькі кольори"</string>
@@ -472,4 +474,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Застосувати"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Підтвердити налаштування"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Деякі налаштування кольорів можуть зробити цей пристрій непридатним для використання. Натисніть OK, щоб підтвердити налаштування, інакше їх буде скинуто через 10 секунд."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Акумулятор (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим економії заряду акумулятора недоступний під час заряджання"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим економії заряду акумулятора"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Знижується продуктивність і обмежується обмін даними у фоновому режимі"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 28484eb..f3dea97 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"خاموشی سے یہ اطلاعات دکھائیں"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"اطلاعات کی فہرست پر سب سے اوپر دکھائیں اور آواز چلائیں"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"اسکرین پر دکھائیں اور آواز چلائیں"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string>
+    <string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"عام رنگ"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"رات کے رنگ"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"حسب ضرورت رنگ"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"لاگو کریں"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"ترتیبات کی توثیق کریں"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"رنگوں کی کچھ ترتیبات اس آلے کو ناقابل استعمال بنا سکتی ہیں۔ رنگوں کی ان ترتیبات کی توثیق کرنے کیلئے ٹھیک ہے پر کلک کریں، بصورت دیگر 10 سیکنڈ بعد یہ ترتیبات ری سیٹ ہو جائیں گی۔"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"بیٹری (%%<xliff:g id="ID_1">%1$d</xliff:g>)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"چارجنگ کے دوران بیٹری سیور دستیاب نہیں ہے"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"بیٹری سیور"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"کارکردگی اور پس منظر کا ڈیٹا کم کر دیتا ہے"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 4bd9dbc..c781082 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Bu bildirishnomalar ovozsiz ko‘rsatilsin"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Bildirishnomalar ro‘yxatining boshida ovoz bilan ko‘rsatilsin"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Barcha oynalar ustida signal ovozi bilan ko‘rsatilsin"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Tayyor"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Odatiy ranglar"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Qoramtir ranglar"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Foydalanuvchi rangi"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Qo‘llash"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Sozlamalarni tasdiqlang"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Ba’zi rang sozlamalari qurilmadan foydalanishni qiyinlashtirish mumkin. Tanlgan parametrlarni tasdiqlash uchun “OK” tugmasini bosing. Aks holda, ular 10 soniyadan so‘ng qayta tiklanadi."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Quvvat (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Quvvat tejash rejimidan quvvatlash vaqtida foydalanib bo‘lmaydi"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Quvvat tejash rejimi"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Unumdorlikni pasaytiradi va fonda internetdan foydalanishni cheklaydi"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c094d5b..38283ad 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Hiển thị im lặng các thông báo này"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Hiển thị ở đầu danh sách thông báo và phát ra âm thanh"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Hiển thị trên màn hình và phát ra âm thanh"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Cài đặt khác"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Xong"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Màu thông thường"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Màu tối"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Màu tùy chỉnh"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Áp dụng"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Xác nhận cài đặt"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Một số cài đặt màu có thể khiến thiết bị này không sử dụng được. Hãy nhấp vào OK để xác nhận các cài đặt màu này, nếu không những cài đặt này sẽ được đặt lại sau 10 giây."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Pin (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Trình tiết kiệm pin không khả dụng trong khi sạc"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Trình tiết kiệm pin"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Giảm hiệu suất và dữ liệu nền"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 3af77c2..8534d82 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -77,7 +77,7 @@
     <string name="usb_preference_title" msgid="6551050377388882787">"USB文件传输选项"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"作为媒体播放器(MTP)装载"</string>
     <string name="use_ptp_button_title" msgid="7517127540301625751">"作为相机(PTP)装载"</string>
-    <string name="installer_cd_button_title" msgid="2312667578562201583">"安装适用于Mac的Android文件传输应用"</string>
+    <string name="installer_cd_button_title" msgid="2312667578562201583">"安装适用于 Mac 的 Android 文件传输应用"</string>
     <string name="accessibility_back" msgid="567011538994429120">"返回"</string>
     <string name="accessibility_home" msgid="8217216074895377641">"主屏幕"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"菜单"</string>
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"显示这些通知,但不发出提示音"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"在通知列表顶部显示,并发出提示音"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"在屏幕上持续显示,并发出提示音"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string>
+    <string name="notification_done" msgid="5279426047273930175">"完成"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"常规颜色"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"夜间颜色"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"自定义颜色"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"应用"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"确认设置"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"部分颜色设置可能会导致此设备无法使用。请点击“确定”确认这些颜色设置,否则,系统将在 10 秒后重置这些设置。"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"电池 (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用节电助手"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"节电助手"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低性能并限制后台流量"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 8591785..0f0ac69 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -454,12 +454,14 @@
     <string name="low_importance" msgid="4109929986107147930">"低重要性"</string>
     <string name="default_importance" msgid="8192107689995742653">"一般重要性"</string>
     <string name="high_importance" msgid="1527066195614050263">"高重要性"</string>
-    <string name="max_importance" msgid="5089005872719563894">"緊急"</string>
+    <string name="max_importance" msgid="5089005872719563894">"緊急重要性"</string>
     <string name="notification_importance_blocked" msgid="2397192642657872872">"永不顯示這些通知"</string>
     <string name="notification_importance_low" msgid="4383563267370859725">"顯示在通知清單底部但不發出音效"</string>
     <string name="notification_importance_default" msgid="4926529615920610817">"顯示這些通知但不發出音效"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"顯示在通知清單頂部並發出音效"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"不時於螢幕出現並發出音效"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string>
+    <string name="notification_done" msgid="5279426047273930175">"完成"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"一般色系"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"深沉色系"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"自訂顏色"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"套用"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"確認設定"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"部分顏色設定會令此裝置無法使用。請按一下 [確定] 加以確認,否則這些顏色設定將於 10 秒後重設。"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"電池電量 (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用「省電模式」"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"省電模式"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低效能並限制背景數據傳輸"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 34fa7aa..3139e41 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"顯示這些通知且不發出任何音效"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"顯示在通知清單頂端並發出音效"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"短暫顯示在螢幕上並發出音效"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string>
+    <string name="notification_done" msgid="5279426047273930175">"完成"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"一般顏色"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"夜間顏色"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"自訂顏色"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"套用"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"確認設定"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"部分顏色設定可能會造成這部裝置無法使用。請按一下 [確定] 來確認您要使用這類顏色設定,否則系統將在 10 秒後重設這些設定。"</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"電池 (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用節約耗電量模式"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"節約耗電量"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低效能並限制背景資料傳輸"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index ebd857c..b93596a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -460,6 +460,8 @@
     <string name="notification_importance_default" msgid="4926529615920610817">"Bonisa ngokuthulile lezi zaziso"</string>
     <string name="notification_importance_high" msgid="3222680136612408223">"Bonisa ngaphezulu kohlu lwezaziso uphinde wenze umsindo"</string>
     <string name="notification_importance_max" msgid="5236987171904756134">"Bheka kusikrini uphinde wenze umsindo"</string>
+    <string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string>
+    <string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string>
     <string name="color_matrix_none" msgid="2121957926040543148">"Imibala ejwayelekile"</string>
     <string name="color_matrix_night" msgid="5943817622105307072">"Imibala yasebusuku"</string>
     <string name="color_matrix_custom" msgid="3655576492322298713">"Imibala yangokwezifiso"</string>
@@ -470,4 +472,8 @@
     <string name="color_apply" msgid="9212602012641034283">"Sebenzisa"</string>
     <string name="color_revert_title" msgid="4746666545480534663">"Qinisekisa izilungiselelo"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"Ezinye izilungiselelo zombala zingenza le divayisi ingasebenziseki. Chofoza ku-KULUNGILE ukuze uqinisekise lezi zilungiselelo zombala, uma kungenjalo lezi zilungiselelo zizosethwa kabusha ngemuva kwamasekhondi angu-10."</string>
+    <string name="battery_panel_title" msgid="3476715163685592453">"Ibhethri (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Isilondolozi sebhethri asitholakali ngesikhathi sokushaja"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"Isilondolozi sebhethri"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Sehlisa ukusebenza nedatha yasemuva"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
index 6102aa6..bf0cba2 100644
--- a/packages/SystemUI/res/values/arrays.xml
+++ b/packages/SystemUI/res/values/arrays.xml
@@ -37,4 +37,18 @@
         <item>157</item><item>334</item>
         <item>0</item>  <item>334</item>
     </array>
+    <array name="batterymeter_plus_points">
+        <item>3</item><item>0</item>
+        <item>5</item><item>0</item>
+        <item>5</item><item>3</item>
+        <item>8</item><item>3</item>
+        <item>8</item><item>5</item>
+        <item>5</item><item>5</item>
+        <item>5</item><item>8</item>
+        <item>3</item><item>8</item>
+        <item>3</item><item>5</item>
+        <item>0</item><item>5</item>
+        <item>0</item><item>3</item>
+        <item>3</item><item>3</item>
+    </array>
 </resources>
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
new file mode 100644
index 0000000..8c760fc
--- /dev/null
+++ b/packages/SystemUI/res/values/arrays_car.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- These should be overriden in an overlay. The default implementation is empty.
+         There needs to be correspondence per index between these arrays, which means that if there
+         isn't a longpress action associated with a shortcut item, put in an empty item to make
+         sure everything lines up.
+    -->
+    <array name="car_facet_icons" />
+    <array name="car_facet_intent_uris" />
+    <array name="car_facet_longpress_intent_uris" />
+    <array name="car_facet_package_filters"/>
+    <array name="car_facet_category_filters"/>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0ccc236..a80a5de 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,7 +36,6 @@
     <color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
     <color name="qs_text">#FFFFFFFF</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
-    <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
     <color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
     <color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200 -->
     <color name="qs_detail_button">#FFB0BEC5</color><!-- 100% blue grey 200 -->
@@ -48,6 +47,7 @@
     <color name="data_usage_graph_warning">#FFFFFFFF</color>
     <color name="status_bar_clock_color">#FFFFFFFF</color>
     <color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
+    <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
 
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
@@ -62,8 +62,6 @@
     <color name="recents_task_bar_light_icon_color">#ffeeeeee</color>
     <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
     <color name="recents_task_bar_dark_icon_color">#99000000</color>
-    <!-- The recents task bar highlight color. -->
-    <color name="recents_task_bar_highlight_color">#28ffffff</color>
     <!-- The lock to task button background color. -->
     <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
     <!-- The lock to task button foreground color. -->
@@ -102,10 +100,9 @@
     <color name="current_user_border_color">@color/system_accent_color</color>
 
     <!-- The "inside" of a notification, reached via longpress -->
-    <color name="notification_guts_bg_color">@*android:color/material_deep_teal_500</color>
-    <color name="notification_guts_title_color">#B2DFDB</color>
-    <color name="notification_guts_text_color">#FFFFFFFF</color>
-    <color name="notification_guts_btn_color">#FFFFFFFF</color>
+    <color name="notification_guts_bg_color">@*android:color/material_grey_50</color>
+    <color name="notification_guts_slider_color">@*android:color/material_deep_teal_500</color>
+    <color name="notification_guts_secondary_slider_color">#858383</color>
 
     <color name="assist_orb_color">#ffffff</color>
 
@@ -149,4 +146,8 @@
 
     <color name="default_remote_input_background">@*android:color/notification_default_color</color>
     <color name="remote_input_hint">#4dffffff</color>
+
+    <color name="qs_tile_tint_unavailable">#40ffffff</color>
+    <color name="qs_tile_tint_inactive">#4dffffff</color>
+    <color name="qs_tile_tint_active">#ffffffff</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 40e8b50..e98ec82 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -172,11 +172,14 @@
     <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
 
     <!-- The animation duration for animating the removal of a task view. -->
-    <integer name="recents_animate_task_view_remove_duration">250</integer>
+    <integer name="recents_animate_task_view_remove_duration">175</integer>
 
     <!-- The animation duration for scrolling the stack to a particular item. -->
     <integer name="recents_animate_task_stack_scroll_duration">200</integer>
 
+    <!-- The animation duration for scrolling the stack to a particular item. -->
+    <integer name="recents_auto_advance_duration">2000</integer>
+
     <!-- The animation duration for entering and exiting the history. -->
     <integer name="recents_history_transition_duration">250</integer>
 
@@ -279,5 +282,8 @@
     <!-- Whether to show the full screen user switcher. -->
     <bool name="config_enableFullscreenUserSwitcher">false</bool>
 
+    <!-- SystemUIFactory component -->
+    <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 035f564..3fb5f18 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -167,6 +167,8 @@
     <dimen name="segmented_button_spacing">0dp</dimen>
     <dimen name="borderless_button_radius">2dp</dimen>
 
+    <dimen name="restricted_padlock_pading">4dp</dimen>
+
     <!-- How far the expanded QS panel peeks from the header in collapsed state. -->
     <dimen name="qs_peek_height">0dp</dimen>
 
@@ -198,16 +200,16 @@
     <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
 
     <!-- The min translation in the Z index for the last task. -->
-    <dimen name="recents_task_view_z_min">20dp</dimen>
+    <dimen name="recents_task_view_z_min">16dp</dimen>
 
     <!-- The max translation in the Z index for the last task. -->
-    <dimen name="recents_task_view_z_max">80dp</dimen>
+    <dimen name="recents_task_view_z_max">48dp</dimen>
 
     <!-- The amount to translate when animating the removal of a task. -->
     <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
 
     <!-- The amount of highlight to make on each task view. -->
-    <dimen name="recents_task_view_highlight">1.5dp</dimen>
+    <dimen name="recents_task_view_highlight">1dp</dimen>
 
     <!-- The amount to offset when animating into an affiliate group. -->
     <dimen name="recents_task_view_affiliate_group_enter_offset">64dp</dimen>
@@ -298,7 +300,7 @@
     <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
 
     <dimen name="notifications_top_padding">4dp</dimen>
-    
+
     <!-- Minimum distance the user has to drag down to go to the full shade. -->
     <dimen name="keyguard_drag_down_min_distance">100dp</dimen>
 
@@ -509,7 +511,7 @@
 
     <!-- The maximum width of the navigation bar ripples. -->
     <dimen name="key_button_ripple_max_width">95dp</dimen>
-    
+
     <!-- Inset shadow for FakeShadowDrawable. It is used to avoid gaps between the card
          and the shadow. -->
     <dimen name="fake_shadow_inset">1dp</dimen>
diff --git a/packages/SystemUI/res/values/internal.xml b/packages/SystemUI/res/values/internal.xml
index 67685ee..e0d3cd2 100644
--- a/packages/SystemUI/res/values/internal.xml
+++ b/packages/SystemUI/res/values/internal.xml
@@ -17,6 +17,7 @@
 <resources>
     <dimen name="status_bar_height">@*android:dimen/status_bar_height</dimen>
     <dimen name="navigation_bar_height">@*android:dimen/navigation_bar_height</dimen>
+    <dimen name="navigation_bar_height_car_mode">@*android:dimen/navigation_bar_height_car_mode</dimen>
     <color name="screen_pinning_primary_text">@*android:color/primary_text_default_material_light</color>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 45ddd50..de49677 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3,16 +3,16 @@
 /**
  * Copyright (c) 2009, 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 
+ * 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 
+ *     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 
+ * 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.
  */
 -->
@@ -1173,6 +1173,11 @@
     <!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
     <string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string>
 
+    <!-- Toggles the fast-toggling indicator. DO NOT TRANSLATE -->
+    <string name="overview_fast_toggle_indicator">Enable fast toggle indicator</string>
+    <!-- Description for the fast-toggling indicator. DO NOT TRANSLATE -->
+    <string name="overview_fast_toggle_indicator_desc">Show an indicator for the launch timeout</string>
+
     <!-- Toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
     <string name="overview_initial_state_paging">Initialize to paging</string>
     <!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
@@ -1228,6 +1233,11 @@
     <!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
     <string name="notification_importance_max">Peek onto the screen and make sound</string>
 
+    <!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] -->
+    <string name="notification_more_settings">More settings</string>
+    <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
+    <string name="notification_done">Done</string>
+
     <!-- Label for no color transform [CHAR LIMIT=30] -->
     <string name="color_matrix_none">Normal colors</string>
 
@@ -1264,4 +1274,25 @@
     <string name="color_modification_g" translatable="false">G</string>
     <string name="color_modification_b" translatable="false">B</string>
 
+    <!-- Title of the battery settings detail panel [CHAR LIMIT=20] -->
+    <string name="battery_panel_title">Battery (<xliff:g name="pattery_percent" example="52">%1$d</xliff:g>%%)</string>
+
+    <!-- Summary of battery saver not available [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_charging_summary">Battery Saver not available during charging</string>
+
+    <!-- Title of switch for battery saver [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_switch_title">Battery Saver</string>
+
+    <!-- Summary of switch for battery saver [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_switch_summary">Reduces performance and background data</string>
+
+    <!-- User visible title for the system-wide keyboard shortcuts list. -->
+    <string name="keyboard_shortcut_group_system">System</string>
+    <!-- User visible title for the keyboard shortcut that takes the user to the home screen. -->
+    <string name="keyboard_shortcut_group_system_home">Home</string>
+    <!-- User visible title for the the keyboard shortcut that takes the user to the recents screen. -->
+    <string name="keyboard_shortcut_group_system_recents">Recents</string>
+    <!-- User visible title for the the keyboard shortcut that triggers the back action. -->
+    <string name="keyboard_shortcut_group_system_back">Back</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
new file mode 100644
index 0000000..d432a62
--- /dev/null
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Button to close PIP on PIP UI -->
+    <string name="pip_exit">Close PIP</string>
+    <!-- Button to move PIP screen to the fullscreen on PIP UI -->
+    <string name="pip_fullscreen">Full screen</string>
+    <!-- Button to play the current media on PIP UI -->
+    <string name="pip_play">Play</string>
+    <!-- Button to pause the current media on PIP UI -->
+    <string name="pip_pause">Pause</string>
+    <!-- Button to close PIP overlay menu on PIP UI -->
+    <string name="pip_cancel">Cancel</string>
+    <!-- Overlay text on PIP -->
+    <string name="pip_hold_home">Hold HOME to control PIP</string>
+</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3b0ab791..4329f78 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -317,4 +317,32 @@
         <item name="@dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
     </style>
 
+    <style name="TextAppearance.NotificationGuts">
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+
+    <style name="TextAppearance.NotificationGuts.Header">
+        <item name="android:alpha">.38</item>
+        <item name="android:textSize">12sp</item>
+    </style>
+
+    <style name="TextAppearance.NotificationGuts.Secondary">
+        <item name="android:alpha">.54</item>
+    </style>
+
+    <style name="TextAppearance.NotificationGuts.Primary">
+        <item name="android:alpha">.87</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="TextAppearance.NotificationGuts.Button">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:gravity">center</item>
+        <item name="android:textColor">@*android:color/material_deep_teal_500</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
new file mode 100644
index 0000000..3f0caab
--- /dev/null
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="PipTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:backgroundDimEnabled">false</item>
+     </style>
+</resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index f02f763..11ef735d 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -80,10 +80,16 @@
             android:summary="@string/overview_initial_state_paging_desc" />
 
         <com.android.systemui.tuner.TunerSwitch
-            android:key="overview_fast_toggle"
+            android:key="overview_fast_toggle_via_button"
             android:title="@string/overview_fast_toggle_via_button"
             android:summary="@string/overview_fast_toggle_via_button_desc" />
 
+        <com.android.systemui.tuner.TunerSwitch
+            android:key="overview_fast_toggle_indicator"
+            android:title="@string/overview_fast_toggle_indicator"
+            android:summary="@string/overview_fast_toggle_indicator_desc"
+            android:dependency="overview_fast_toggle_via_button" />
+
     </PreferenceScreen>
 
     <SwitchPreference
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 3eb1271..38ae345 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -49,7 +49,8 @@
     private float mButtonHeightFraction;
     private float mSubpixelSmoothingLeft;
     private float mSubpixelSmoothingRight;
-    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
+    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint,
+            mPlusPaint;
     private float mTextHeight, mWarningTextHeight;
     private int mIconTint = Color.WHITE;
 
@@ -60,10 +61,13 @@
     private int mChargeColor;
     private final float[] mBoltPoints;
     private final Path mBoltPath = new Path();
+    private final float[] mPlusPoints;
+    private final Path mPlusPath = new Path();
 
     private final RectF mFrame = new RectF();
     private final RectF mButtonFrame = new RectF();
     private final RectF mBoltFrame = new RectF();
+    private final RectF mPlusFrame = new RectF();
 
     private final Path mShapePath = new Path();
     private final Path mClipPath = new Path();
@@ -141,6 +145,9 @@
         mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
         mBoltPoints = loadBoltPoints(res);
 
+        mPlusPaint = new Paint(mBoltPaint);
+        mPlusPoints = loadPlusPoints(res);
+
         mDarkModeBackgroundColor =
                 context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
         mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
@@ -187,8 +194,8 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
-        mPowerSaveEnabled = mBatteryController.isPowerSave();
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mPowerSaveEnabled = isPowerSave;
         invalidateSelf();
     }
 
@@ -207,6 +214,21 @@
         return ptsF;
     }
 
+    private static float[] loadPlusPoints(Resources res) {
+        final int[] pts = res.getIntArray(R.array.batterymeter_plus_points);
+        int maxX = 0, maxY = 0;
+        for (int i = 0; i < pts.length; i += 2) {
+            maxX = Math.max(maxX, pts[i]);
+            maxY = Math.max(maxY, pts[i + 1]);
+        }
+        final float[] ptsF = new float[pts.length];
+        for (int i = 0; i < pts.length; i += 2) {
+            ptsF[i] = (float)pts[i] / maxX;
+            ptsF[i + 1] = (float)pts[i + 1] / maxY;
+        }
+        return ptsF;
+    }
+
     @Override
     public void setBounds(int left, int top, int right, int bottom) {
         super.setBounds(left, top, right, bottom);
@@ -328,9 +350,9 @@
 
         if (mPluggedIn) {
             // define the bolt shape
-            final float bl = mFrame.left + mFrame.width() / 4.5f;
+            final float bl = mFrame.left + mFrame.width() / 4f;
             final float bt = mFrame.top + mFrame.height() / 6f;
-            final float br = mFrame.right - mFrame.width() / 7f;
+            final float br = mFrame.right - mFrame.width() / 4f;
             final float bb = mFrame.bottom - mFrame.height() / 10f;
             if (mBoltFrame.left != bl || mBoltFrame.top != bt
                     || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
@@ -358,6 +380,39 @@
                 // otherwise cut the bolt out of the overall shape
                 mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
             }
+        } else if (mPowerSaveEnabled) {
+            // define the plus shape
+            final float pw = mFrame.width() * 2 / 3;
+            final float pl = mFrame.left + (mFrame.width() - pw) / 2;
+            final float pt = mFrame.top + (mFrame.height() - pw) / 2;
+            final float pr = mFrame.right - (mFrame.width() - pw) / 2;
+            final float pb = mFrame.bottom - (mFrame.height() - pw) / 2;
+            if (mPlusFrame.left != pl || mPlusFrame.top != pt
+                    || mPlusFrame.right != pr || mPlusFrame.bottom != pb) {
+                mPlusFrame.set(pl, pt, pr, pb);
+                mPlusPath.reset();
+                mPlusPath.moveTo(
+                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
+                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
+                for (int i = 2; i < mPlusPoints.length; i += 2) {
+                    mPlusPath.lineTo(
+                            mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(),
+                            mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height());
+                }
+                mPlusPath.lineTo(
+                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
+                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
+            }
+
+            float boltPct = (mPlusFrame.bottom - levelTop) / (mPlusFrame.bottom - mPlusFrame.top);
+            boltPct = Math.min(Math.max(boltPct, 0), 1);
+            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
+                // draw the bolt if opaque
+                c.drawPath(mPlusPath, mPlusPaint);
+            } else {
+                // otherwise cut the bolt out of the overall shape
+                mShapePath.op(mPlusPath, Path.Op.DIFFERENCE);
+            }
         }
 
         // compute percentage text
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 6cb8da4..cdbdc22 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 7f6cda0..99028a6c 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -32,7 +32,7 @@
     /**
      * Docks the top-most task and opens recents.
      */
-    void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds);
+    boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds);
 
     /**
      * Called during a drag-from-navbar-in gesture.
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 870e0af..fabc7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -86,6 +86,7 @@
     final private int[] mTmpPos = new int[2];
     private int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
+    private boolean mDisableHwLayers;
 
     public SwipeHelper(int swipeDirection, Callback callback, Context context) {
         mCallback = callback;
@@ -115,6 +116,10 @@
         mPagingTouchSlop = pagingTouchSlop;
     }
 
+    public void setDisableHardwareLayers(boolean disableHwLayers) {
+        mDisableHwLayers = disableHwLayers;
+    }
+
     private float getPos(MotionEvent ev) {
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
@@ -147,7 +152,7 @@
         }
     }
 
-    private float getSize(View v) {
+    protected float getSize(View v) {
         return mSwipeDirection == X ? v.getMeasuredWidth() :
                 v.getMeasuredHeight();
     }
@@ -178,10 +183,12 @@
         if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
             if (FADE_OUT_DURING_SWIPE && dismissable) {
                 float alpha = swipeProgress;
-                if (alpha != 0f && alpha != 1f) {
-                    animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                } else {
-                    animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                if (!mDisableHwLayers) {
+                    if (alpha != 0f && alpha != 1f) {
+                        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                    } else {
+                        animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                    }
                 }
                 animView.setAlpha(getSwipeProgressForOffset(animView));
             }
@@ -345,7 +352,9 @@
             duration = fixedDuration;
         }
 
-        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        if (!mDisableHwLayers) {
+            animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        }
         ObjectAnimator anim = createTranslationAnimation(animView, newPos);
         if (useAccelerateInterpolator) {
             anim.setInterpolator(mFastOutLinearInInterpolator);
@@ -362,7 +371,9 @@
                 if (endAction != null) {
                     endAction.run();
                 }
-                animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                if (!mDisableHwLayers) {
+                    animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 19e299254..472648a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -54,6 +54,7 @@
             com.android.systemui.power.PowerUI.class,
             com.android.systemui.media.RingtonePlayer.class,
             com.android.systemui.keyboard.KeyboardUI.class,
+            com.android.systemui.tv.pip.PipUI.class
     };
 
     /**
@@ -80,6 +81,8 @@
         // the theme set there.
         setTheme(R.style.systemui_theme);
 
+        SystemUIFactory.createFromConfig(this);
+
         if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
             IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
             filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
new file mode 100644
index 0000000..681b39e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -0,0 +1,69 @@
+/*
+ * 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.systemui;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+
+/**
+ * Class factory to provide customizable SystemUI components.
+ */
+public class SystemUIFactory {
+    private static final String TAG = "SystemUIFactory";
+
+    static SystemUIFactory mFactory;
+
+    public static SystemUIFactory getInstance() {
+        return mFactory;
+    }
+
+    public static void createFromConfig(Context context) {
+        final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
+        if (clsName == null || clsName.length() == 0) {
+            throw new RuntimeException("No SystemUIFactory component configured");
+        }
+
+        try {
+            Class<?> cls = null;
+            cls = context.getClassLoader().loadClass(clsName);
+            mFactory = (SystemUIFactory) cls.newInstance();
+        } catch (Throwable t) {
+            Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
+            throw new RuntimeException(t);
+        }
+    }
+
+    public SystemUIFactory() {}
+
+    public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
+            ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
+        return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
+    }
+
+    public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback,
+            LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
+            ViewGroup container) {
+        return new KeyguardBouncer(context, callback, lockPatternUtils, windowManager, container);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 443778e..9a00b4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -69,6 +69,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -474,10 +475,12 @@
 
     ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
 
+        @Override
         public void userActivity() {
             KeyguardViewMediator.this.userActivity();
         }
 
+        @Override
         public void keyguardDone(boolean strongAuth) {
             if (!mKeyguardDonePending) {
                 KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
@@ -487,6 +490,7 @@
             }
         }
 
+        @Override
         public void keyguardDoneDrawing() {
             mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
         }
@@ -588,8 +592,9 @@
         updateInputRestrictedLocked();
         mTrustManager.reportKeyguardShowingChanged();
 
-        mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext,
-                mViewMediatorCallback, mLockPatternUtils);
+        mStatusBarKeyguardViewManager =
+                SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
+                        mViewMediatorCallback, mLockPatternUtils);
         final ContentResolver cr = mContext.getContentResolver();
 
         mDeviceInteractive = mPM.isInteractive();
@@ -741,8 +746,7 @@
 
         long timeout;
 
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
-        if ((!user.isManagedProfile() && LockPatternUtils.isSeparateWorkChallengeEnabled())
+        if ((mLockPatternUtils.isSeparateProfileChallengeEnabled(userId))
                 || policyTimeout <= 0) {
             timeout = lockAfterTimeout;
         } else {
@@ -782,9 +786,9 @@
     private void doKeyguardLaterLockedForChildProfiles() {
         UserManager um = UserManager.get(mContext);
         List<UserInfo> profiles = um.getEnabledProfiles(UserHandle.myUserId());
-        if (LockPatternUtils.isSeparateWorkChallengeEnabled() && profiles.size() > 1) {
+        if (profiles.size() > 1) {
             for (UserInfo info : profiles) {
-                if (info.id != UserHandle.myUserId() && info.isManagedProfile()) {
+                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) {
                     long userTimeout = getLockTimeout(info.id);
                     long userWhen = SystemClock.elapsedRealtime() + userTimeout;
                     Intent lockIntent = new Intent(DELAYED_LOCK_PROFILE_ACTION);
@@ -1248,7 +1252,9 @@
                 if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
                         + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
                 synchronized (KeyguardViewMediator.this) {
-                    doKeyguardLocked(null);
+                    if (mDelayedShowingSequence == sequence) {
+                        doKeyguardLocked(null);
+                    }
                 }
             } else if (DELAYED_LOCK_PROFILE_ACTION.equals(intent.getAction())) {
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, 0);
@@ -1698,6 +1704,7 @@
         mHandler.removeMessages(KEYGUARD_DONE_PENDING_TIMEOUT);
     }
 
+    @Override
     public void onBootCompleted() {
         mUpdateMonitor.dispatchBootCompleted();
         synchronized (this) {
@@ -1731,10 +1738,15 @@
     public void onActivityDrawn() {
         mHandler.sendEmptyMessage(ON_ACTIVITY_DRAWN);
     }
+
     public ViewMediatorCallback getViewMediatorCallback() {
         return mViewMediatorCallback;
     }
 
+    public LockPatternUtils getLockPatternUtils() {
+        return mLockPatternUtils;
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("  mSystemReady: "); pw.println(mSystemReady);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 19b65f7..ea1c9bf 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -52,7 +52,6 @@
 
     private static final int SHOWING_NOTHING = 0;
     private static final int SHOWING_WARNING = 1;
-    private static final int SHOWING_SAVER = 2;
     private static final int SHOWING_INVALID_CHARGER = 3;
     private static final String[] SHOWING_STRINGS = {
         "SHOWING_NOTHING",
@@ -63,7 +62,6 @@
 
     private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
     private static final String ACTION_START_SAVER = "PNW.startSaver";
-    private static final String ACTION_STOP_SAVER = "PNW.stopSaver";
     private static final String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning";
 
     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
@@ -77,7 +75,6 @@
     private final Handler mHandler = new Handler();
     private final Receiver mReceiver = new Receiver();
     private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
-    private final Intent mOpenSaverSettings = settings(Settings.ACTION_BATTERY_SAVER_SETTINGS);
 
     private int mBatteryLevel;
     private int mBucket;
@@ -86,7 +83,6 @@
 
     private long mBucketDroppedNegativeTimeMs;
 
-    private boolean mSaver;
     private boolean mWarning;
     private boolean mPlaySound;
     private boolean mInvalidCharger;
@@ -101,7 +97,6 @@
 
     @Override
     public void dump(PrintWriter pw) {
-        pw.print("mSaver="); pw.println(mSaver);
         pw.print("mWarning="); pw.println(mWarning);
         pw.print("mPlaySound="); pw.println(mPlaySound);
         pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
@@ -121,27 +116,15 @@
         mScreenOffTime = screenOffTime;
     }
 
-    @Override
-    public void showSaverMode(boolean mode) {
-        mSaver = mode;
-        if (mSaver && mSaverConfirmation != null) {
-            mSaverConfirmation.dismiss();
-        }
-        updateNotification();
-    }
-
     private void updateNotification() {
         if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
-                + mPlaySound + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
+                + mPlaySound + " mInvalidCharger=" + mInvalidCharger);
         if (mInvalidCharger) {
             showInvalidChargerNotification();
             mShowing = SHOWING_INVALID_CHARGER;
         } else if (mWarning) {
             showWarningNotification();
             mShowing = SHOWING_WARNING;
-        } else if (mSaver) {
-            showSaverNotification();
-            mShowing = SHOWING_SAVER;
         } else {
             mNoMan.cancelAsUser(TAG_NOTIFICATION, R.id.notification_power, UserHandle.ALL);
             mShowing = SHOWING_NOTHING;
@@ -165,8 +148,7 @@
     }
 
     private void showWarningNotification() {
-        final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
-                : R.string.battery_low_percent_format;
+        final int textRes = R.string.battery_low_percent_format;
         final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
         final Notification.Builder nb = new Notification.Builder(mContext)
                 .setSmallIcon(R.drawable.ic_power_low)
@@ -184,13 +166,9 @@
         if (hasBatterySettings()) {
             nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
         }
-        if (!mSaver) {
-            nb.addAction(0,
-                    mContext.getString(R.string.battery_saver_start_action),
-                    pendingBroadcast(ACTION_START_SAVER));
-        } else {
-            addStopSaverAction(nb);
-        }
+        nb.addAction(0,
+                mContext.getString(R.string.battery_saver_start_action),
+                pendingBroadcast(ACTION_START_SAVER));
         if (mPlaySound) {
             attachLowBatterySound(nb);
             mPlaySound = false;
@@ -199,35 +177,6 @@
         mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL);
     }
 
-    private void showSaverNotification() {
-        final Notification.Builder nb = new Notification.Builder(mContext)
-                .setSmallIcon(R.drawable.ic_power_saver)
-                .setContentTitle(mContext.getString(R.string.battery_saver_notification_title))
-                .setContentText(mContext.getString(R.string.battery_saver_notification_text))
-                .setOngoing(true)
-                .setShowWhen(false)
-                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.battery_saver_mode_color));
-        addStopSaverAction(nb);
-        if (hasSaverSettings()) {
-            nb.setContentIntent(pendingActivity(mOpenSaverSettings));
-        }
-        mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL);
-    }
-
-    private void addStopSaverAction(Notification.Builder nb) {
-        nb.addAction(0,
-                mContext.getString(R.string.battery_saver_notification_action_text),
-                pendingBroadcast(ACTION_STOP_SAVER));
-    }
-
-    private void dismissSaverNotification() {
-        if (mSaver) Slog.i(TAG, "dismissing saver notification");
-        mSaver = false;
-        updateNotification();
-    }
-
     private PendingIntent pendingActivity(Intent intent) {
         return PendingIntent.getActivityAsUser(mContext,
                 0, intent, 0, null, UserHandle.CURRENT);
@@ -272,10 +221,6 @@
         return mOpenBatterySettings.resolveActivity(mContext.getPackageManager()) != null;
     }
 
-    private boolean hasSaverSettings() {
-        return mOpenSaverSettings.resolveActivity(mContext.getPackageManager()) != null;
-    }
-
     @Override
     public void showLowBatteryWarning(boolean playSound) {
         Slog.i(TAG,
@@ -367,7 +312,6 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
             filter.addAction(ACTION_START_SAVER);
-            filter.addAction(ACTION_STOP_SAVER);
             filter.addAction(ACTION_DISMISSED_WARNING);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
                     android.Manifest.permission.STATUS_BAR_SERVICE, mHandler);
@@ -383,10 +327,6 @@
             } else if (action.equals(ACTION_START_SAVER)) {
                 dismissLowBatteryNotification();
                 showStartSaverConfirmation();
-            } else if (action.equals(ACTION_STOP_SAVER)) {
-                dismissSaverNotification();
-                dismissLowBatteryNotification();
-                setSaverMode(false);
             } else if (action.equals(ACTION_DISMISSED_WARNING)) {
                 dismissLowBatteryWarning();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 9459740..522d533 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -76,10 +76,6 @@
         mReceiver.init();
     }
 
-    private void setSaverMode(boolean mode) {
-        mWarnings.showSaverMode(mode);
-    }
-
     void updateBatteryWarningLevels() {
         int critLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
@@ -141,11 +137,6 @@
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
             mContext.registerReceiver(this, filter, null, mHandler);
-            updateSaverMode();
-        }
-
-        private void updateSaverMode() {
-            setSaverMode(mPowerManager.isPowerSaveMode());
         }
 
         @Override
@@ -210,10 +201,6 @@
                 mScreenOffTime = -1;
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 mWarnings.userSwitched();
-            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
-                updateSaverMode();
-            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
-                setSaverMode(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
             } else {
                 Slog.w(TAG, "unknown intent: " + intent);
             }
@@ -251,7 +238,6 @@
 
     public interface WarningsUI {
         void update(int batteryLevel, int bucket, long screenOffTime);
-        void showSaverMode(boolean mode);
         void dismissLowBatteryWarning();
         void showLowBatteryWarning(boolean playSound);
         void dismissInvalidChargerWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index b56ad76..7651ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -75,12 +75,17 @@
             iv.setTag(R.id.qs_icon_tag, state.icon);
             if (d instanceof Animatable) {
                 Animatable a = (Animatable) d;
-                if (state.icon instanceof QSTile.AnimationIcon && !iv.isShown()) {
+                a.start();
+                if (!iv.isShown()) {
                     a.stop(); // skip directly to end state
                 }
             }
         }
-
+        if (state.disabledByPolicy) {
+            iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
+        } else {
+            iv.clearColorFilter();
+        }
     }
 
     protected int getIconMeasureMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 16fd9eb..fd07e50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -329,16 +329,19 @@
                     drawTile(r, state);
                 }
             }
+
             @Override
             public void onShowDetail(boolean show) {
                 QSPanel.this.showDetail(show, r);
             }
+
             @Override
             public void onToggleStateChanged(boolean state) {
                 if (mDetailRecord == r) {
                     fireToggleStateChanged(state);
                 }
             }
+
             @Override
             public void onScanStateChanged(boolean state) {
                 r.scanState = state;
@@ -352,7 +355,7 @@
                 announceForAccessibility(announcement);
             }
         };
-        r.tile.setCallback(callback);
+        r.tile.addCallback(callback);
         final View.OnClickListener click = new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -415,6 +418,9 @@
     }
 
     protected void handleShowDetail(Record r, boolean show) {
+        if (show && !mExpanded) {
+            mHost.animateExpandQS();
+        }
         if (r instanceof TileRecord) {
             handleShowDetailTile((TileRecord) r, show);
         } else {
@@ -466,7 +472,7 @@
             MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_quick_settings_detail,
-                    mContext.getString(detailAdapter.getTitle())));
+                    detailAdapter.getTitle()));
             setDetailRecord(r);
             listener = mHideGridContentWhenDone;
             if (r instanceof TileRecord && visibleDiff) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 39f0c55..de7c02d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -19,23 +19,39 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.qs.QSTile.State;
-import com.android.systemui.statusbar.policy.*;
+import com.android.systemui.qs.external.TileServices;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.Listenable;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Objects;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 /**
  * Base quick-settings tile, extend this to create a new tile.
  *
@@ -52,7 +68,7 @@
     protected final H mHandler;
     protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
 
-    private Callback mCallback;
+    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     protected TState mState = newTileState();
     private TState mTmpState = newTileState();
     private boolean mAnnounceNextStateChange;
@@ -99,7 +115,7 @@
     }
 
     public interface DetailAdapter {
-        int getTitle();
+        CharSequence getTitle();
         Boolean getToggleState();
         View createDetailView(Context context, View convertView, ViewGroup parent);
         Intent getSettingsIntent();
@@ -109,8 +125,8 @@
 
     // safe to call from any thread
 
-    public void setCallback(Callback callback) {
-        mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
+    public void addCallback(Callback callback) {
+        mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
     }
 
     public void click() {
@@ -167,8 +183,8 @@
 
     // call only on tile worker looper
 
-    private void handleSetCallback(Callback callback) {
-        mCallback = callback;
+    private void handleAddCallback(Callback callback) {
+        mCallbacks.add(callback);
         handleRefreshState(null);
     }
 
@@ -196,12 +212,14 @@
 
     private void handleStateChanged() {
         boolean delayAnnouncement = shouldAnnouncementBeDelayed();
-        if (mCallback != null) {
-            mCallback.onStateChanged(mState);
+        if (mCallbacks.size() != 0) {
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                mCallbacks.get(i).onStateChanged(mState);
+            }
             if (mAnnounceNextStateChange && !delayAnnouncement) {
                 String announcement = composeChangeAnnouncement();
                 if (announcement != null) {
-                    mCallback.onAnnouncementRequested(announcement);
+                    mCallbacks.get(0).onAnnouncementRequested(announcement);
                 }
             }
         }
@@ -217,20 +235,20 @@
     }
 
     private void handleShowDetail(boolean show) {
-        if (mCallback != null) {
-            mCallback.onShowDetail(show);
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onShowDetail(show);
         }
     }
 
     private void handleToggleStateChanged(boolean state) {
-        if (mCallback != null) {
-            mCallback.onToggleStateChanged(state);
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onToggleStateChanged(state);
         }
     }
 
     private void handleScanStateChanged(boolean state) {
-        if (mCallback != null) {
-            mCallback.onScanStateChanged(state);
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            mCallbacks.get(i).onScanStateChanged(state);
         }
     }
 
@@ -240,11 +258,23 @@
 
     protected void handleDestroy() {
         setListening(false);
-        mCallback = null;
+        mCallbacks.clear();
+    }
+
+    protected void checkIfRestrictionEnforced(State state, String userRestriction) {
+        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+                userRestriction, UserHandle.myUserId());
+        if (admin != null) {
+            state.disabledByPolicy = true;
+            state.enforcedAdmin = admin;
+        } else {
+            state.disabledByPolicy = false;
+            state.enforcedAdmin = null;
+        }
     }
 
     protected final class H extends Handler {
-        private static final int SET_CALLBACK = 1;
+        private static final int ADD_CALLBACK = 1;
         private static final int CLICK = 2;
         private static final int SECONDARY_CLICK = 3;
         private static final int LONG_CLICK = 4;
@@ -264,13 +294,19 @@
         public void handleMessage(Message msg) {
             String name = null;
             try {
-                if (msg.what == SET_CALLBACK) {
-                    name = "handleSetCallback";
-                    handleSetCallback((QSTile.Callback)msg.obj);
+                if (msg.what == ADD_CALLBACK) {
+                    name = "handleAddCallback";
+                    handleAddCallback((QSTile.Callback)msg.obj);
                 } else if (msg.what == CLICK) {
                     name = "handleClick";
-                    mAnnounceNextStateChange = true;
-                    handleClick();
+                    if (mState.disabledByPolicy) {
+                        Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+                                mContext, mState.enforcedAdmin);
+                        mHost.startActivityDismissingKeyguard(intent);
+                    } else {
+                        mAnnounceNextStateChange = true;
+                        handleClick();
+                    }
                 } else if (msg.what == SECONDARY_CLICK) {
                     name = "handleSecondaryClick";
                     handleSecondaryClick();
@@ -323,6 +359,7 @@
         void startRunnableDismissingKeyguard(Runnable runnable);
         void warn(String message, Throwable t);
         void collapsePanels();
+        void animateExpandQS();
         void openPanels();
         Looper getLooper();
         Context getContext();
@@ -340,8 +377,10 @@
         UserSwitcherController getUserSwitcherController();
         UserInfoController getUserInfoController();
         BatteryController getBatteryController();
+        TileServices getTileServices();
         void removeTile(String tileSpec);
 
+
         public interface Callback {
             void onTilesChanged();
         }
@@ -389,11 +428,7 @@
 
         @Override
         public Drawable getDrawable(Context context) {
-            Drawable d = context.getDrawable(mResId);
-            if (d instanceof Animatable) {
-                ((Animatable) d).start();
-            }
-            return d;
+            return context.getDrawable(mResId);
         }
 
         @Override
@@ -408,41 +443,14 @@
     }
 
     protected class AnimationIcon extends ResourceIcon {
-        private boolean mAllowAnimation;
-
         public AnimationIcon(int resId) {
             super(resId);
         }
 
-        public void setAllowAnimation(boolean allowAnimation) {
-            mAllowAnimation = allowAnimation;
-        }
-
         @Override
         public Drawable getDrawable(Context context) {
             // workaround: get a clean state for every new AVD
-            final AnimatedVectorDrawable d = (AnimatedVectorDrawable) context.getDrawable(mResId)
-                    .getConstantState().newDrawable();
-            d.start();
-            if (mAllowAnimation) {
-                mAllowAnimation = false;
-            } else {
-                d.stop(); // skip directly to end state
-            }
-            return d;
-        }
-    }
-
-    protected enum UserBoolean {
-        USER_TRUE(true, true),
-        USER_FALSE(true, false),
-        BACKGROUND_TRUE(false, true),
-        BACKGROUND_FALSE(false, false);
-        public final boolean value;
-        public final boolean userInitiated;
-        private UserBoolean(boolean userInitiated, boolean value) {
-            this.value = value;
-            this.userInitiated = userInitiated;
+            return context.getDrawable(mResId).getConstantState().newDrawable();
         }
     }
 
@@ -452,6 +460,8 @@
         public CharSequence contentDescription;
         public CharSequence dualLabelContentDescription;
         public boolean autoMirrorDrawable = true;
+        public boolean disabledByPolicy;
+        public EnforcedAdmin enforcedAdmin;
 
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
@@ -461,12 +471,16 @@
                     || !Objects.equals(other.contentDescription, contentDescription)
                     || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
                     || !Objects.equals(other.dualLabelContentDescription,
-                    dualLabelContentDescription);
+                    dualLabelContentDescription)
+                    || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
+                    || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
             other.icon = icon;
             other.label = label;
             other.contentDescription = contentDescription;
             other.dualLabelContentDescription = dualLabelContentDescription;
             other.autoMirrorDrawable = autoMirrorDrawable;
+            other.disabledByPolicy = disabledByPolicy;
+            enforcedAdmin.copyTo(other.enforcedAdmin);
             return changed;
         }
 
@@ -482,6 +496,8 @@
             sb.append(",contentDescription=").append(contentDescription);
             sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
             sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
+            sb.append(",disabledByPolicy=").append(disabledByPolicy);
+            sb.append(",enforcedAdmin=").append(enforcedAdmin);
             return sb.append(']');
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
deleted file mode 100644
index a5e1fd5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.android.systemui.qs;
-
-import android.os.IBinder;
-import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.Tile;
-import android.util.Log;
-
-
-public class QSTileServiceWrapper implements IQSTileService {
-    private static final String TAG = "IQSTileServiceWrapper";
-
-    private final IQSTileService mService;
-    
-    public QSTileServiceWrapper(IQSTileService service) {
-        mService = service;
-    }
-
-    @Override
-    public IBinder asBinder() {
-        return mService.asBinder();
-    }
-
-    @Override
-    public void setQSTile(Tile tile) {
-        try {
-            mService.setQSTile(tile);
-        } catch (Exception e) {
-            Log.d(TAG, "Caught exception from QSTileService", e);
-        }
-    }
-
-    @Override
-    public void onTileAdded() {
-        try {
-            mService.onTileAdded();
-        } catch (Exception e) {
-            Log.d(TAG, "Caught exception from QSTileService", e);
-        }
-    }
-
-    @Override
-    public void onTileRemoved() {
-        try {
-            mService.onTileRemoved();
-        } catch (Exception e) {
-            Log.d(TAG, "Caught exception from QSTileService", e);
-        }
-    }
-
-    @Override
-    public void onStartListening() {
-        try {
-            mService.onStartListening();
-        } catch (Exception e) {
-            Log.d(TAG, "Caught exception from QSTileService", e);
-        }
-    }
-
-    @Override
-    public void onStopListening() {
-        try {
-            mService.onStopListening();
-        } catch (Exception e) {
-            Log.d(TAG, "Caught exception from QSTileService", e);
-        }
-    }
-
-    @Override
-    public void onClick(IBinder token) {
-        try {
-            mService.onClick(token);
-        } catch (Exception e) {
-            Log.d(TAG, "Caught exception from QSTileService", e);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 41ac4d9..664ca39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -19,32 +19,33 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Typeface;
 import android.util.MathUtils;
-import android.util.TypedValue;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.TextView;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
-    private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
-            Typeface.NORMAL);
-
     protected final Context mContext;
+    private QSIconView mIconView;
     private final int mTileSpacingPx;
     private int mTilePaddingTopPx;
 
     private TextView mLabel;
+    private ImageView mPadLock;
 
     public QSTileView(Context context, QSIconView icon) {
         super(context, icon);
 
         mContext = context;
+        mIconView = icon;
         final Resources res = context.getResources();
         mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
+
         setClipChildren(false);
 
         setClickable(true);
@@ -76,16 +77,10 @@
 
     private void createLabel() {
         final Resources res = mContext.getResources();
-        mLabel = new TextView(mContext);
-        mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
-        mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
-        mLabel.setMinLines(2);
-        mLabel.setPadding(0, 0, 0, 0);
-        mLabel.setTypeface(CONDENSED);
-        mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
-                res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
-        mLabel.setClickable(false);
-        addView(mLabel);
+        View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
+        mLabel = (TextView) view.findViewById(R.id.tile_label);
+        mPadLock = (ImageView) view.findViewById(R.id.restricted_padlock);
+        addView(view);
     }
 
     public void init(OnClickListener clickPrimary, OnLongClickListener longClick) {
@@ -96,5 +91,7 @@
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
         mLabel.setText(state.label);
+        mLabel.setEnabled(!state.disabledByPolicy);
+        mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index bda4675..5782800 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -49,20 +49,19 @@
         mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
     }
 
+    @Override
+    protected void createCustomizePanel() {
+        // No customizing from the header.
+    }
+
     public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
         mFullPanel = fullPanel;
         mHeader = header;
     }
 
     @Override
-    protected void handleShowDetail(QSPanel.Record r, boolean show) {
-        if (show) {
-            mHeader.performClick();
-            mFullPanel.showDetail(show, r);
-        } else {
-            // Not sure how we would end up here...
-            super.handleShowDetail(r, show);
-        }
+    protected void showDetail(boolean show, Record r) {
+        // Do nothing, will be handled by the QSPanel.
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
index 95ff611..ac4f05f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
@@ -19,8 +19,9 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-
+import android.graphics.drawable.Drawable;
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 
 public class BlankCustomTile extends QSTile<QSTile.State> {
@@ -72,7 +73,9 @@
         try {
             PackageManager pm = mContext.getPackageManager();
             ServiceInfo info = pm.getServiceInfo(mComponent, 0);
-            state.icon = new DrawableIcon(info.loadIcon(pm));
+            Drawable drawable = info.loadIcon(pm);
+            drawable.setTint(mContext.getColor(R.color.qs_tile_tint_active));
+            state.icon = new DrawableIcon(drawable);
             state.label = info.loadLabel(pm).toString();
             state.contentDescription = state.label;
         } catch (Exception e) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 87c2973..eab4dca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -16,32 +16,28 @@
 package com.android.systemui.qs.customize;
 
 import android.app.ActivityManager;
-import android.app.Service;
 import android.content.ClipData;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
-import android.service.quicksettings.IQSTileService;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileServiceWrapper;
-import com.android.systemui.qs.tiles.CustomTile;
+import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.TileLifecycleManager;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import com.android.systemui.tuner.TunerService;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -52,8 +48,9 @@
 public class CustomQSPanel extends QSPanel {
     
     private static final String TAG = "CustomQSPanel";
+    private static final boolean DEBUG = false;
 
-    private List<String> mSavedTiles;
+    private List<String> mSavedTiles = Collections.emptyList();
     private ArrayList<String> mStash;
     private List<String> mTiles = new ArrayList<>();
 
@@ -67,17 +64,30 @@
         ((NonPagedTileLayout) mTileLayout).setCustomQsPanel(this);
         removeView(mFooter.getView());
 
+        if (DEBUG) Log.d(TAG, "new CustomQSPanel", new Throwable());
         TunerService.get(mContext).addTunable(this, QSTileHost.TILES_SETTING);
     }
 
     @Override
+    protected void showDetail(boolean show, Record r) {
+        // No detail here.
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        // Don't allow the super to unregister the tunable.
+    }
+
+    @Override
     public void onTuningChanged(String key, String newValue) {
         if (key.equals(QS_SHOW_BRIGHTNESS)) {
             // No Brightness for you.
             super.onTuningChanged(key, "0");
         }
         if (QSTileHost.TILES_SETTING.equals(key)) {
-            mSavedTiles = QSTileHost.loadTileSpecs(mContext, newValue);
+            mSavedTiles = Collections.unmodifiableList(
+                    QSTileHost.loadTileSpecs(mContext, newValue));
+            if (DEBUG) Log.d(TAG, "New saved tiles " + TextUtils.join(",", mSavedTiles));
         }
     }
 
@@ -106,6 +116,7 @@
     }
 
     public void setSavedTiles() {
+        if (DEBUG) Log.d(TAG, "setSavedTiles " + TextUtils.join(",", mSavedTiles));
         setTiles(mSavedTiles);
     }
 
@@ -114,47 +125,26 @@
             String tileSpec = mSavedTiles.get(i);
             if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
             if (!mTiles.contains(tileSpec)) {
-                mContext.bindServiceAsUser(
-                        new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec)),
-                        new ServiceConnection() {
-                            @Override
-                            public void onServiceDisconnected(ComponentName name) {
-                            }
-
-                            @Override
-                            public void onServiceConnected(ComponentName name, IBinder service) {
-                                QSTileServiceWrapper wrapper = new QSTileServiceWrapper(
-                                        IQSTileService.Stub.asInterface(service));
-                                wrapper.onStopListening();
-                                wrapper.onTileRemoved();
-                                mContext.unbindService(this);
-                            }
-                        }, Service.BIND_AUTO_CREATE,
-                        new UserHandle(ActivityManager.getCurrentUser()));
+                Intent intent = new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec));
+                TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
+                        mContext, intent, new UserHandle(ActivityManager.getCurrentUser()));
+                lifecycleManager.onStopListening();
+                lifecycleManager.onTileRemoved();
+                lifecycleManager.flushMessagesAndUnbind();
             }
         }
         for (int i = 0; i < mTiles.size(); i++) {
             String tileSpec = mTiles.get(i);
             if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
             if (!mSavedTiles.contains(tileSpec)) {
-                mContext.bindServiceAsUser(
-                        new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec)),
-                        new ServiceConnection() {
-                            @Override
-                            public void onServiceDisconnected(ComponentName name) {
-                            }
-
-                            @Override
-                            public void onServiceConnected(ComponentName name, IBinder service) {
-                                QSTileServiceWrapper wrapper = new QSTileServiceWrapper(
-                                        IQSTileService.Stub.asInterface(service));
-                                wrapper.onTileAdded();
-                                mContext.unbindService(this);
-                            }
-                        }, Service.BIND_AUTO_CREATE,
-                        new UserHandle(ActivityManager.getCurrentUser()));
+                Intent intent = new Intent().setComponent(CustomTile.getComponentFromSpec(tileSpec));
+                TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
+                        mContext, intent, new UserHandle(ActivityManager.getCurrentUser()));
+                lifecycleManager.onTileAdded();
+                lifecycleManager.flushMessagesAndUnbind();
             }
         }
+        if (DEBUG) Log.d(TAG, "saveCurrentTiles " + mTiles);
         Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
                 TextUtils.join(",", mTiles), ActivityManager.getCurrentUser());
     }
@@ -173,30 +163,37 @@
     }
 
     private void setTilesInternal() {
+        if (DEBUG) Log.d(TAG, "Set tiles internal");
         for (int i = 0; i < mCurrentTiles.size(); i++) {
             mCurrentTiles.get(i).destroy();
         }
         mCurrentTiles.clear();
         for (int i = 0; i < mTiles.size(); i++) {
             if (mTiles.get(i).startsWith(CustomTile.PREFIX)) {
-                mCurrentTiles.add(BlankCustomTile.create(mHost, mTiles.get(i)));
+                QSTile<?> tile = BlankCustomTile.create(mHost, mTiles.get(i));
+                tile.setTileSpec(mTiles.get(i));
+                mCurrentTiles.add(tile);
             } else {
                 QSTile<?> tile = mHost.createTile(mTiles.get(i));
                 if (tile != null) {
+                    tile.setTileSpec(mTiles.get(i));
                     mCurrentTiles.add(tile);
+                } else {
+                    if (DEBUG) Log.d(TAG, "Skipping " + mTiles.get(i));
                 }
             }
-            mCurrentTiles.get(mCurrentTiles.size() - 1).setTileSpec(mTiles.get(i));
         }
         super.setTiles(mCurrentTiles);
     }
 
     public void addTile(String spec) {
+        if (DEBUG) Log.d(TAG, "addTile " + spec);
         mTiles.add(spec);
         setTilesInternal();
     }
 
     public void moveTo(String from, String to) {
+        if (DEBUG) Log.d(TAG, "moveTo " + from + " " + to);
         int fromIndex = mTiles.indexOf(from);
         if (fromIndex < 0) {
             Log.e(TAG, "Unknown from tile " + from);
@@ -220,6 +217,7 @@
     }
 
     public void setTiles(List<String> tiles) {
+        if (DEBUG) Log.d(TAG, "Set tiles " + TextUtils.join(",", tiles));
         mTiles = new ArrayList<>(tiles);
         setTilesInternal();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 6706c7a..a6a7143 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -41,7 +41,7 @@
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTile.Icon;
-import com.android.systemui.qs.tiles.CustomTile;
+import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.statusbar.phone.QSTileHost;
 import com.android.systemui.tuner.QSPagingSwitch;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
rename to packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index bb74f34..d398b64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -11,32 +11,29 @@
  * 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.
+ * limitations under the License
  */
+package com.android.systemui.qs.external;
 
-package com.android.systemui.qs.tiles;
-
-import android.app.ActivityManager;
-import android.app.Service;
 import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileServiceWrapper;
 import com.android.systemui.statusbar.phone.QSTileHost;
 
 public class CustomTile extends QSTile<QSTile.State> {
@@ -52,8 +49,9 @@
     private final Tile mTile;
     private final IWindowManager mWindowManager;
     private final IBinder mToken = new Binder();
+    private final IQSTileService mService;
+    private final TileServiceManager mServiceManager;
 
-    private QSTileServiceWrapper mService;
     private boolean mListening;
     private boolean mBound;
     private boolean mIsTokenGranted;
@@ -63,7 +61,9 @@
         super(host);
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
         mComponent = ComponentName.unflattenFromString(action);
-        mTile = new Tile(mComponent, host);
+        mServiceManager = host.getTileServices().getTileWrapper(this);
+        mService = mServiceManager.getTileService();
+        mTile = new Tile(mComponent);
         try {
             PackageManager pm = mContext.getPackageManager();
             ServiceInfo info = pm.getServiceInfo(mComponent, 0);
@@ -72,6 +72,11 @@
             mTile.setLabel(info.loadLabel(pm));
         } catch (Exception e) {
         }
+        try {
+            mService.setQSTile(mTile);
+        } catch (RemoteException e) {
+            // Called through wrapper, won't happen here.
+        }
     }
 
     public ComponentName getComponent() {
@@ -86,6 +91,7 @@
         mTile.setIcon(tile.getIcon());
         mTile.setLabel(tile.getLabel());
         mTile.setContentDescription(tile.getContentDescription());
+        mTile.setState(tile.getState());
     }
 
     public void onDialogShown() {
@@ -96,42 +102,33 @@
     public void setListening(boolean listening) {
         if (mListening == listening) return;
         mListening = listening;
-        if (listening) {
-            mHandler.removeCallbacks(mUnbind);
-            if (!mBound) {
-                // TODO: Guarantee re-bind on user-switch.
-                mContext.bindServiceAsUser(new Intent().setComponent(mComponent),
-                        mServiceConnection, Service.BIND_AUTO_CREATE,
-                        new UserHandle(ActivityManager.getCurrentUser()));
-                mBound = true;
-            } else {
-                if (mService != null) {
+        try {
+            if (listening) {
+                if (mServiceManager.getType() == TileService.TILE_MODE_PASSIVE) {
+                    mServiceManager.setBindRequested(true);
                     mService.onStartListening();
-                } else {
-                    Log.d(TAG, "Can't start service listening");
                 }
-            }
-        } else {
-            if (mService != null) {
+            } else {
                 mService.onStopListening();
-            }
-            if (mIsTokenGranted && !mIsShowingDialog) {
-                try {
-                    if (DEBUG) Log.d(TAG, "Removing token");
-                    mWindowManager.removeWindowToken(mToken);
-                } catch (RemoteException e) {
+                if (mIsTokenGranted && !mIsShowingDialog) {
+                    try {
+                        if (DEBUG) Log.d(TAG, "Removing token");
+                        mWindowManager.removeWindowToken(mToken);
+                    } catch (RemoteException e) {
+                    }
+                    mIsTokenGranted = false;
                 }
-                mIsTokenGranted = false;
+                mIsShowingDialog = false;
+                mServiceManager.setBindRequested(false);
             }
-            mIsShowingDialog = false;
-            mHandler.postDelayed(mUnbind, UNBIND_DELAY);
+        } catch (RemoteException e) {
+            // Called through wrapper, won't happen here.
         }
     }
 
     @Override
     protected void handleDestroy() {
         super.handleDestroy();
-        mHandler.removeCallbacks(mUnbind);
         if (mIsTokenGranted) {
             try {
                 if (DEBUG) Log.d(TAG, "Removing token");
@@ -139,7 +136,7 @@
             } catch (RemoteException e) {
             }
         }
-        mUnbind.run();
+        mHost.getTileServices().freeService(this, mServiceManager);
     }
 
     @Override
@@ -154,16 +151,23 @@
 
     @Override
     protected void handleClick() {
-        if (mService != null) {
-            try {
-                if (DEBUG) Log.d(TAG, "Adding token");
-                mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG);
-                mIsTokenGranted = true;
-            } catch (RemoteException e) {
+        if (mTile.getState() == Tile.STATE_UNAVAILABLE) {
+            return;
+        }
+        try {
+            if (DEBUG) Log.d(TAG, "Adding token");
+            mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG);
+            mIsTokenGranted = true;
+        } catch (RemoteException e) {
+        }
+        try {
+            if (mServiceManager.getType() == TileService.TILE_MODE_ACTIVE) {
+                mServiceManager.setBindRequested(true);
+                mService.onStartListening();
             }
             mService.onClick(mToken);
-        } else {
-            Log.e(TAG, "Click with no service " + getTileSpec());
+        } catch (RemoteException e) {
+            // Called through wrapper, won't happen here.
         }
         MetricsLogger.action(mContext, getMetricsCategory(), mComponent.getPackageName());
     }
@@ -175,9 +179,15 @@
     @Override
     protected void handleUpdateState(State state, Object arg) {
         Drawable drawable = mTile.getIcon().loadDrawable(mContext);
-        drawable.setTint(mContext.getColor(android.R.color.white));
+        int color = mContext.getColor(getColor(mTile.getState()));
+        drawable.setTint(color);
         state.icon = new DrawableIcon(drawable);
         state.label = mTile.getLabel();
+        if (mTile.getState() == Tile.STATE_UNAVAILABLE) {
+            state.label = new SpannableStringBuilder().append(state.label,
+                    new ForegroundColorSpan(color),
+                    SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
+        }
         if (mTile.getContentDescription() != null) {
             state.contentDescription = mTile.getContentDescription();
         } else {
@@ -187,33 +197,32 @@
 
     @Override
     public int getMetricsCategory() {
-        return MetricsLogger.QS_INTENT;
+        return MetricsLogger.QS_CUSTOM;
     }
 
-    private final ServiceConnection mServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mService = new QSTileServiceWrapper(IQSTileService.Stub.asInterface(service));
-            if (mListening) {
-                mService.setQSTile(mTile);
-                mService.onStartListening();
-            } else {
-                mService.onStopListening();
+    public void startUnlockAndRun() {
+        mHost.startRunnableDismissingKeyguard(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    mService.onUnlockComplete();
+                } catch (RemoteException e) {
+                }
             }
-        }
+        });
+    }
 
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
+    private static int getColor(int state) {
+        switch (state) {
+            case Tile.STATE_UNAVAILABLE:
+                return R.color.qs_tile_tint_unavailable;
+            case Tile.STATE_INACTIVE:
+                return R.color.qs_tile_tint_inactive;
+            case Tile.STATE_ACTIVE:
+                return R.color.qs_tile_tint_active;
         }
-    };
-
-    private final Runnable mUnbind = new Runnable() {
-        @Override
-        public void run() {
-            mContext.unbindService(mServiceConnection);
-            mBound = false;
-        }
-    };
+        return 0;
+    }
 
     public static ComponentName getComponentFromSpec(String spec) {
         final String action = spec.substring(PREFIX.length(), spec.length() - 1);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
new file mode 100644
index 0000000..3830ac5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.os.IBinder;
+import android.service.quicksettings.IQSService;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.util.Log;
+
+
+public class QSTileServiceWrapper {
+    private static final String TAG = "IQSTileServiceWrapper";
+
+    private final IQSTileService mService;
+
+    public QSTileServiceWrapper(IQSTileService service) {
+        mService = service;
+    }
+
+    public IBinder asBinder() {
+        return mService.asBinder();
+    }
+
+    public boolean setQSTile(Tile tile) {
+        try {
+            mService.setQSTile(tile);
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean onTileAdded() {
+        try {
+            mService.onTileAdded();
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean onTileRemoved() {
+        try {
+            mService.onTileRemoved();
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean onStartListening() {
+        try {
+            mService.onStartListening();
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean onStopListening() {
+        try {
+            mService.onStopListening();
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean onClick(IBinder token) {
+        try {
+            mService.onClick(token);
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean setQSService(IQSService service) {
+        try {
+            mService.setQSService(service);
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+
+    public boolean onUnlockComplete() {
+        try {
+            mService.onUnlockComplete();
+            return true;
+        } catch (Exception e) {
+            Log.d(TAG, "Caught exception from TileService", e);
+            return false;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
new file mode 100644
index 0000000..4977d80
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.app.AppGlobals;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSService;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
+import android.util.Log;
+import libcore.util.Objects;
+
+import java.util.Set;
+
+/**
+ * Manages the lifecycle of a TileService.
+ * <p>
+ * Will keep track of all calls on the IQSTileService interface and will relay those calls to the
+ * TileService as soon as it is bound.  It will only bind to the service when it is allowed to
+ * ({@link #setBindService(boolean)}) and when the service is available.
+ */
+public class TileLifecycleManager extends BroadcastReceiver implements
+        IQSTileService, ServiceConnection, IBinder.DeathRecipient {
+    public static final boolean DEBUG = false;
+
+    private static final String TAG = "TileLifecycleManager";
+
+    private static final int MSG_ON_ADDED = 0;
+    private static final int MSG_ON_REMOVED = 1;
+    private static final int MSG_ON_CLICK = 2;
+    private static final int MSG_ON_UNLOCK_COMPLETE = 3;
+
+    // Bind retry control.
+    private static final int MAX_BIND_RETRIES = 5;
+    private static final int BIND_RETRY_DELAY = 1000;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final Intent mIntent;
+    private final UserHandle mUser;
+
+    private Set<Integer> mQueuedMessages = new ArraySet<>();
+    private QSTileServiceWrapper mWrapper;
+    private boolean mListening;
+    private Tile mTile;
+    private IBinder mClickBinder;
+
+    private int mBindTryCount;
+    private boolean mBound;
+    @VisibleForTesting
+    boolean mReceiverRegistered;
+    private IQSService mService;
+    private boolean mUnbindImmediate;
+
+    public TileLifecycleManager(Handler handler, Context context, Intent intent, UserHandle user) {
+        mContext = context;
+        mHandler = handler;
+        mIntent = intent;
+        mUser = user;
+    }
+
+    public ComponentName getComponent() {
+        return mIntent.getComponent();
+    }
+
+    public boolean hasPendingClick() {
+        synchronized (mQueuedMessages) {
+            return mQueuedMessages.contains(MSG_ON_CLICK);
+        }
+    }
+
+    /**
+     * Binds just long enough to send any queued messages, then unbinds.
+     */
+    public void flushMessagesAndUnbind() {
+        mUnbindImmediate = true;
+        setBindService(true);
+    }
+
+    public void setBindService(boolean bind) {
+        mBound = bind;
+        if (bind) {
+            if (mBindTryCount == MAX_BIND_RETRIES) {
+                // Too many failures, give up on this tile until an update.
+                startPackageListening();
+                return;
+            }
+            if (!checkComponentState()) {
+                return;
+            }
+            if (DEBUG) Log.d(TAG, "Binding service " + mIntent);
+            mBindTryCount++;
+            mContext.bindServiceAsUser(mIntent, this,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                    mUser);
+        } else {
+            if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent);
+            // Give it another chance next time it needs to be bound, out of kindness.
+            mBindTryCount = 0;
+            mWrapper = null;
+            mContext.unbindService(this);
+        }
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {
+        if (DEBUG) Log.d(TAG, "onServiceConnected " + name);
+        // Got a connection, set the binding count to 0.
+        mBindTryCount = 0;
+        mWrapper = new QSTileServiceWrapper(Stub.asInterface(service));
+        try {
+            service.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+        }
+        setQSService(mService);
+        setQSTile(mTile);
+        handlePendingMessages();
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
+        mWrapper = null;
+    }
+
+    private void handlePendingMessages() {
+        // This ordering is laid out manually to make sure we preserve the TileService
+        // lifecycle.
+        ArraySet<Integer> queue;
+        synchronized (mQueuedMessages) {
+            queue = new ArraySet<>(mQueuedMessages);
+            mQueuedMessages.clear();
+        }
+        if (queue.contains(MSG_ON_ADDED)) {
+            if (DEBUG) Log.d(TAG, "Handling pending onAdded");
+            onTileAdded();
+        }
+        if (mListening) {
+            if (DEBUG) Log.d(TAG, "Handling pending onStartListening");
+            onStartListening();
+        }
+        if (queue.contains(MSG_ON_CLICK)) {
+            if (DEBUG) Log.d(TAG, "Handling pending onClick");
+            if (!mListening) {
+                Log.w(TAG, "Managed to get click on non-listening state...");
+                // Skipping click since lost click privileges.
+            } else {
+                onClick(mClickBinder);
+            }
+        }
+        if (queue.contains(MSG_ON_UNLOCK_COMPLETE)) {
+            if (DEBUG) Log.d(TAG, "Handling pending onUnlockComplete");
+            if (!mListening) {
+                Log.w(TAG, "Managed to get unlock on non-listening state...");
+                // Skipping unlock since lost click privileges.
+            } else {
+                onUnlockComplete();
+            }
+        }
+        if (queue.contains(MSG_ON_REMOVED)) {
+            if (DEBUG) Log.d(TAG, "Handling pending onRemoved");
+            if (mListening) {
+                Log.w(TAG, "Managed to get remove in listening state...");
+                onStopListening();
+            }
+            onTileRemoved();
+        }
+        if (mUnbindImmediate) {
+            mUnbindImmediate = false;
+            setBindService(false);
+        }
+    }
+
+    public void handleDestroy() {
+        if (DEBUG) Log.d(TAG, "handleDestroy");
+        if (mReceiverRegistered) {
+            stopPackageListening();
+        }
+    }
+
+    private void handleDeath() {
+        if (mWrapper == null) return;
+        mWrapper = null;
+        if (!mBound) return;
+        if (DEBUG) Log.d(TAG, "handleDeath");
+        if (checkComponentState()) {
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    if (mBound) {
+                        // Retry binding.
+                        setBindService(true);
+                    }
+                }
+            }, BIND_RETRY_DELAY);
+        }
+    }
+
+    @Override
+    public void setQSTile(Tile tile) {
+        if (DEBUG) Log.d(TAG, "setQSTile " + tile);
+        mTile = tile;
+        if (mWrapper != null && !mWrapper.setQSTile(tile)) {
+            handleDeath();
+        }
+    }
+
+    private boolean checkComponentState() {
+        PackageManager pm = mContext.getPackageManager();
+        if (!isPackageAvailable(pm) || !isComponentAvailable(pm)) {
+            startPackageListening();
+            return false;
+        }
+        return true;
+    }
+
+    private void startPackageListening() {
+        if (DEBUG) Log.d(TAG, "startPackageListening");
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        mContext.registerReceiverAsUser(this, mUser, filter, null, mHandler);
+        mReceiverRegistered = true;
+    }
+
+    private void stopPackageListening() {
+        if (DEBUG) Log.d(TAG, "stopPackageListening");
+        mContext.unregisterReceiver(this);
+        mReceiverRegistered = false;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (DEBUG) Log.d(TAG, "onReceive: " + intent);
+        Uri data = intent.getData();
+        String pkgName = data.getEncodedSchemeSpecificPart();
+        if (!Objects.equal(pkgName, mIntent.getComponent().getPackageName())) {
+            return;
+        }
+        stopPackageListening();
+        if (mBound) {
+            // Trying to bind again will check the state of the package before bothering to bind.
+            if (DEBUG) Log.d(TAG, "Trying to rebind");
+            setBindService(true);
+        }
+    }
+
+    private boolean isComponentAvailable(PackageManager pm) {
+        String packageName = mIntent.getComponent().getPackageName();
+        try {
+            ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(mIntent.getComponent(),
+                    0, mUser.getIdentifier());
+            if (DEBUG && si == null) Log.d(TAG, "Can't find component " + mIntent.getComponent());
+            return si != null;
+        } catch (RemoteException e) {
+            // Shouldn't happen.
+        }
+        return false;
+    }
+
+    private boolean isPackageAvailable(PackageManager pm) {
+        String packageName = mIntent.getComponent().getPackageName();
+        try {
+            pm.getPackageInfoAsUser(packageName, 0, mUser.getIdentifier());
+            return true;
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) Log.d(TAG, "Package not available: " + packageName, e);
+            else Log.d(TAG, "Package not available: " + packageName);
+        }
+        return false;
+    }
+
+    private void queueMessage(int message) {
+        synchronized (mQueuedMessages) {
+            mQueuedMessages.add(message);
+        }
+    }
+
+    @Override
+    public void setQSService(IQSService service) {
+        mService = service;
+        if (mWrapper == null || !mWrapper.setQSService(service)) {
+            handleDeath();
+        }
+    }
+
+    @Override
+    public void onTileAdded() {
+        if (DEBUG) Log.d(TAG, "onTileAdded");
+        if (mWrapper == null || !mWrapper.onTileAdded()) {
+            queueMessage(MSG_ON_ADDED);
+            handleDeath();
+        }
+    }
+
+    @Override
+    public void onTileRemoved() {
+        if (DEBUG) Log.d(TAG, "onTileRemoved");
+        if (mWrapper == null || !mWrapper.onTileRemoved()) {
+            queueMessage(MSG_ON_REMOVED);
+            handleDeath();
+        }
+    }
+
+    @Override
+    public void onStartListening() {
+        if (DEBUG) Log.d(TAG, "onStartListening");
+        mListening = true;
+        if (mWrapper != null && !mWrapper.onStartListening()) {
+            handleDeath();
+        }
+    }
+
+    @Override
+    public void onStopListening() {
+        if (DEBUG) Log.d(TAG, "onStopListening");
+        mListening = false;
+        if (mWrapper != null && !mWrapper.onStopListening()) {
+            handleDeath();
+        }
+    }
+
+    @Override
+    public void onClick(IBinder iBinder) {
+        if (DEBUG) Log.d(TAG, "onClick " + iBinder);
+        if (mWrapper == null || !mWrapper.onClick(iBinder)) {
+            mClickBinder = iBinder;
+            queueMessage(MSG_ON_CLICK);
+            handleDeath();
+        }
+    }
+
+    @Override
+    public void onUnlockComplete() {
+        if (DEBUG) Log.d(TAG, "onUnlockComplete");
+        if (mWrapper == null || !mWrapper.onUnlockComplete()) {
+            queueMessage(MSG_ON_UNLOCK_COMPLETE);
+            handleDeath();
+        }
+    }
+
+    @Override
+    public IBinder asBinder() {
+        return mWrapper != null ? mWrapper.asBinder() : null;
+    }
+
+    @Override
+    public void binderDied() {
+        if (DEBUG) Log.d(TAG, "binderDeath");
+        handleDeath();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
new file mode 100644
index 0000000..2f77a30
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.TileService;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+/**
+ * Manages the priority which lets {@link TileServices} make decisions about which tiles
+ * to bind.  Also holds on to and manages the {@link TileLifecycleManager}, informing it
+ * of when it is allowed to bind based on decisions frome the {@link TileServices}.
+ */
+public class TileServiceManager {
+
+    private static final long MIN_BIND_TIME = 5000;
+    private static final long UNBIND_DELAY = 30000;
+
+    public static final boolean DEBUG = true;
+
+    private static final String TAG = "TileServiceManager";
+
+    @VisibleForTesting
+    static final String PREFS_FILE = "CustomTileModes";
+
+    private final TileServices mServices;
+    private final TileLifecycleManager mStateManager;
+    private final Handler mHandler;
+    private boolean mBindRequested;
+    private boolean mBindAllowed;
+    private boolean mBound;
+    private int mPriority;
+    private boolean mJustBound;
+    private long mLastUpdate;
+    private int mType;
+
+    TileServiceManager(TileServices tileServices, Handler handler, ComponentName component) {
+        this(tileServices, handler, new TileLifecycleManager(handler,
+                tileServices.getContext(), new Intent().setComponent(component),
+                new UserHandle(ActivityManager.getCurrentUser())));
+    }
+
+    @VisibleForTesting
+    TileServiceManager(TileServices tileServices, Handler handler,
+            TileLifecycleManager tileLifecycleManager) {
+        mServices = tileServices;
+        mHandler = handler;
+        mStateManager = tileLifecycleManager;
+        mType = tileServices.getContext().getSharedPreferences(PREFS_FILE, 0)
+                .getInt(tileLifecycleManager.getComponent().flattenToString(),
+                        TileService.TILE_MODE_UNSET);
+        mStateManager.setQSService(tileServices);
+        if (mType == TileService.TILE_MODE_UNSET) {
+            bindService();
+            mStateManager.onTileAdded();
+        }
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public void setType(int type) {
+        mServices.getContext().getSharedPreferences(PREFS_FILE, 0).edit()
+                .putInt(mStateManager.getComponent().flattenToString(), type).commit();
+        mType = type;
+        mServices.recalculateBindAllowance();
+    }
+
+    public IQSTileService getTileService() {
+        return mStateManager;
+    }
+
+    public void setBindRequested(boolean bindRequested) {
+        if (mBindRequested == bindRequested) return;
+        mBindRequested = bindRequested;
+        if (mBindAllowed && mBindRequested && !mBound) {
+            mHandler.removeCallbacks(mUnbind);
+            bindService();
+        } else {
+            mServices.recalculateBindAllowance();
+        }
+        if (mBound && !mBindRequested) {
+            mHandler.postDelayed(mUnbind, UNBIND_DELAY);
+        }
+    }
+
+    public void setLastUpdate(long lastUpdate) {
+        mLastUpdate = lastUpdate;
+        if (mBound && mType == TileService.TILE_MODE_ACTIVE) {
+            mStateManager.onStopListening();
+            setBindRequested(false);
+        }
+        mServices.recalculateBindAllowance();
+    }
+
+    public void handleDestroy() {
+        mStateManager.handleDestroy();
+    }
+
+    public void setBindAllowed(boolean allowed) {
+        if (mBindAllowed == allowed) return;
+        mBindAllowed = allowed;
+        if (!mBindAllowed && mBound) {
+            unbindService();
+        } else if (mBindAllowed && mBindRequested && !mBound) {
+            bindService();
+        }
+    }
+
+    private void bindService() {
+        if (mBound) {
+            Log.e(TAG, "Service already bound");
+            return;
+        }
+        mBound = true;
+        mJustBound = true;
+        mHandler.postDelayed(mJustBoundOver, MIN_BIND_TIME);
+        mStateManager.setBindService(true);
+    }
+
+    private void unbindService() {
+        if (!mBound) {
+            Log.e(TAG, "Service not bound");
+            return;
+        }
+        mBound = false;
+        mJustBound = false;
+        mStateManager.setBindService(false);
+    }
+
+    public void calculateBindPriority(long currentTime) {
+        if (mStateManager.hasPendingClick()) {
+            // Pending click is the most important thing, need to put this service at the top of
+            // the list to be bound.
+            mPriority = Integer.MAX_VALUE;
+        } else if (mJustBound) {
+            // If we just bound, lets not thrash on binding/unbinding too much, this is second most
+            // important.
+            mPriority = Integer.MAX_VALUE - 1;
+        } else if (!mBindRequested) {
+            // Don't care about binding right now, put us last.
+            mPriority = Integer.MIN_VALUE;
+        } else {
+            // Order based on whether this was just updated.
+            long timeSinceUpdate = currentTime - mLastUpdate;
+            // Fit compare into integer space for simplicity. Make sure to leave MAX_VALUE and
+            // MAX_VALUE - 1 for the more important states above.
+            if (timeSinceUpdate > Integer.MAX_VALUE - 2) {
+                mPriority = Integer.MAX_VALUE - 2;
+            } else {
+                mPriority = (int) timeSinceUpdate;
+            }
+        }
+    }
+
+    public int getBindPriority() {
+        return mPriority;
+    }
+
+    private final Runnable mUnbind = new Runnable() {
+        @Override
+        public void run() {
+            if (mBound && !mBindRequested) {
+                unbindService();
+            }
+        }
+    };
+
+    @VisibleForTesting
+    final Runnable mJustBoundOver = new Runnable() {
+        @Override
+        public void run() {
+            mJustBound = false;
+            mServices.recalculateBindAllowance();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
new file mode 100644
index 0000000..44d8776
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSService;
+import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * Runs the day-to-day operations of which tiles should be bound and when.
+ */
+public class TileServices extends IQSService.Stub {
+    static final int DEFAULT_MAX_BOUND = 3;
+    static final int REDUCED_MAX_BOUND = 1;
+
+    private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
+    private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
+    private final Context mContext;
+    private final Handler mHandler;
+    private final Handler mMainHandler;
+    private final QSTileHost mHost;
+
+    private int mMaxBound = DEFAULT_MAX_BOUND;
+
+    public TileServices(QSTileHost host, Looper looper) {
+        mHost = host;
+        mContext = mHost.getContext();
+        mContext.registerReceiver(mRequestListeningReceiver,
+                new IntentFilter(TileService.ACTION_REQUEST_LISTENING));
+        mHandler = new Handler(looper);
+        mMainHandler = new Handler(Looper.getMainLooper());
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public TileServiceManager getTileWrapper(CustomTile tile) {
+        ComponentName component = tile.getComponent();
+        TileServiceManager service = onCreateTileService(component);
+        synchronized (mServices) {
+            mServices.put(tile, service);
+            mTiles.put(component, tile);
+        }
+        return service;
+    }
+
+    protected TileServiceManager onCreateTileService(ComponentName component) {
+        return new TileServiceManager(this, mHandler, component);
+    }
+
+    public void freeService(CustomTile tile, TileServiceManager service) {
+        synchronized (mServices) {
+            service.setBindAllowed(false);
+            mServices.remove(tile);
+            mTiles.remove(tile.getComponent());
+            final String slot = tile.getComponent().getClassName();
+            mMainHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mHost.getIconController().removeIcon(slot);
+                }
+            });
+        }
+    }
+
+    public void setMemoryPressure(boolean memoryPressure) {
+        mMaxBound = memoryPressure ? REDUCED_MAX_BOUND : DEFAULT_MAX_BOUND;
+        recalculateBindAllowance();
+    }
+
+    public void recalculateBindAllowance() {
+        final ArrayList<TileServiceManager> services;
+        synchronized (mServices) {
+            services = new ArrayList<>(mServices.values());
+        }
+        final int N = services.size();
+        if (N > mMaxBound) {
+            long currentTime = System.currentTimeMillis();
+            // Precalculate the priority of services for binding.
+            for (int i = 0; i < N; i++) {
+                services.get(i).calculateBindPriority(currentTime);
+            }
+            // Sort them so we can bind the most important first.
+            Collections.sort(services, SERVICE_SORT);
+        }
+        int i;
+        // Allow mMaxBound items to bind.
+        for (i = 0; i < mMaxBound && i < N; i++) {
+            services.get(i).setBindAllowed(true);
+        }
+        // The rest aren't allowed to bind for now.
+        while (i < N) {
+            services.get(i).setBindAllowed(false);
+            i++;
+        }
+    }
+
+    private void verifyCaller(String packageName) {
+        try {
+            int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
+                    Binder.getCallingUserHandle().getIdentifier());
+            if (Binder.getCallingUid() != uid) {
+                throw new SecurityException("Component outside caller's uid");
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new SecurityException(e);
+        }
+    }
+
+    private void requestListening(ComponentName component) {
+        synchronized (mServices) {
+            CustomTile customTile = getTileForComponent(component);
+            if (customTile == null) {
+                Log.d("TileServices", "Couldn't find tile for " + component);
+                return;
+            }
+            TileServiceManager service = mServices.get(customTile);
+            if (service.getType() != TileService.TILE_MODE_ACTIVE) {
+                return;
+            }
+            service.setBindRequested(true);
+            try {
+                service.getTileService().onStartListening();
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    @Override
+    public void setTileMode(ComponentName component, int mode) {
+        verifyCaller(component.getPackageName());
+        CustomTile customTile = getTileForComponent(component);
+        if (customTile != null) {
+            synchronized (mServices) {
+                mServices.get(customTile).setType(mode);
+            }
+        }
+    }
+
+    @Override
+    public void updateQsTile(Tile tile) {
+        ComponentName componentName = tile.getComponentName();
+        verifyCaller(componentName.getPackageName());
+        CustomTile customTile = getTileForComponent(componentName);
+        if (customTile != null) {
+            synchronized (mServices) {
+                mServices.get(customTile).setLastUpdate(System.currentTimeMillis());
+            }
+            customTile.updateState(tile);
+            customTile.refreshState();
+        }
+    }
+
+    @Override
+    public void onShowDialog(Tile tile) {
+        ComponentName componentName = tile.getComponentName();
+        verifyCaller(componentName.getPackageName());
+        CustomTile customTile = getTileForComponent(componentName);
+        if (customTile != null) {
+            customTile.onDialogShown();
+            mHost.collapsePanels();
+        }
+    }
+
+    @Override
+    public void onStartActivity(Tile tile) {
+        ComponentName componentName = tile.getComponentName();
+        verifyCaller(componentName.getPackageName());
+        CustomTile customTile = getTileForComponent(componentName);
+        if (customTile != null) {
+            mHost.collapsePanels();
+        }
+    }
+
+    @Override
+    public void updateStatusIcon(Tile tile, Icon icon, String contentDescription) {
+        final ComponentName componentName = tile.getComponentName();
+        String packageName = componentName.getPackageName();
+        verifyCaller(packageName);
+        CustomTile customTile = getTileForComponent(componentName);
+        if (customTile != null) {
+            try {
+                UserHandle userHandle = getCallingUserHandle();
+                PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(packageName, 0,
+                        userHandle.getIdentifier());
+                if (info.applicationInfo.isSystemApp()) {
+                    final StatusBarIcon statusIcon = icon != null
+                            ? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
+                                    contentDescription)
+                            : null;
+                    mMainHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            StatusBarIconController iconController = mHost.getIconController();
+                            iconController.setIcon(componentName.getClassName(), statusIcon);
+                            iconController.setExternalIcon(componentName.getClassName());
+                        }
+                    });
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+        }
+    }
+
+    @Override
+    public void startUnlockAndRun(Tile tile) {
+        ComponentName componentName = tile.getComponentName();
+        verifyCaller(componentName.getPackageName());
+        CustomTile customTile = getTileForComponent(componentName);
+        if (customTile != null) {
+            customTile.startUnlockAndRun();
+        }
+    }
+
+    @Override
+    public boolean isLocked() {
+        KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
+        return keyguardMonitor.isShowing();
+    }
+
+    @Override
+    public boolean isSecure() {
+        KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
+        return keyguardMonitor.isSecure() && keyguardMonitor.isShowing();
+    }
+
+    private CustomTile getTileForComponent(ComponentName component) {
+        synchronized (mServices) {
+            return mTiles.get(component);
+        }
+    }
+
+    private final BroadcastReceiver mRequestListeningReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (TileService.ACTION_REQUEST_LISTENING.equals(intent.getAction())) {
+                requestListening(
+                        (ComponentName) intent.getParcelableExtra(TileService.EXTRA_COMPONENT));
+            }
+        }
+    };
+
+    private static final Comparator<TileServiceManager> SERVICE_SORT =
+            new Comparator<TileServiceManager>() {
+        @Override
+        public int compare(TileServiceManager left, TileServiceManager right) {
+            return -Integer.compare(left.getBindPriority(), right.getBindPriority());
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index fc802dd..c696f88 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -58,8 +58,6 @@
     public void handleClick() {
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         setEnabled(!mState.value);
-        mEnable.setAllowAnimation(true);
-        mDisable.setAllowAnimation(true);
     }
 
     private void setEnabled(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 84eac65..60238fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,7 +19,14 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Checkable;
+import android.widget.ImageView;
+import android.widget.TextView;
 import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.BatteryInfo;
 import com.android.systemui.BatteryMeterDrawable;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -31,8 +38,11 @@
 
     private final BatteryMeterDrawable mDrawable;
     private final BatteryController mBatteryController;
+    private final BatteryDetail mBatteryDetail = new BatteryDetail();
 
     private int mLevel;
+    private boolean mPowerSave;
+    private boolean mCharging;
 
     public BatteryTile(Host host) {
         super(host);
@@ -48,6 +58,11 @@
     }
 
     @Override
+    public DetailAdapter getDetailAdapter() {
+        return mBatteryDetail;
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsLogger.QS_BATTERY_TILE;
     }
@@ -64,8 +79,16 @@
     }
 
     @Override
+    public void setDetailListening(boolean listening) {
+        super.setDetailListening(listening);
+        if (!listening) {
+            mBatteryDetail.mCurrentView = null;
+        }
+    }
+
+    @Override
     protected void handleClick() {
-        mHost.startActivityDismissingKeyguard(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+        showDetail(true);
     }
 
     @Override
@@ -85,11 +108,98 @@
     @Override
     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
         mLevel = level;
+        mCharging = charging;
         refreshState((Integer) level);
+        if (mBatteryDetail.mCurrentView != null) {
+            mBatteryDetail.bindView();
+        }
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mPowerSave = isPowerSave;
+        if (mBatteryDetail.mCurrentView != null) {
+            mBatteryDetail.bindView();
+        }
+    }
 
+    private final class BatteryDetail implements DetailAdapter, View.OnClickListener {
+        private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(),
+                new Handler(), mHost.getContext().getColor(R.color.batterymeter_frame_color));
+        private View mCurrentView;
+
+        @Override
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.battery_panel_title, mLevel);
+        }
+
+        @Override
+        public Boolean getToggleState() {
+            return null;
+        }
+
+        @Override
+        public View createDetailView(Context context, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(mContext).inflate(R.layout.battery_detail, parent,
+                        false);
+            }
+            mCurrentView = convertView;
+            bindView();
+            return convertView;
+        }
+
+        private void bindView() {
+            mDrawable.onBatteryLevelChanged(100, false, false);
+            mDrawable.onPowerSaveChanged(true);
+            ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
+            Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
+            checkbox.setChecked(mPowerSave);
+            if (mCharging) {
+                BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
+                    @Override
+                    public void onBatteryInfoLoaded(BatteryInfo info) {
+                        if (mCurrentView != null && mCharging) {
+                            ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
+                                    info.mChargeLabelString);
+                        }
+                    }
+                });
+                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
+                        R.string.battery_detail_charging_summary);
+                mCurrentView.setClickable(false);
+                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.INVISIBLE);
+                mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.INVISIBLE);
+            } else {
+                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
+                        R.string.battery_detail_switch_title);
+                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
+                        R.string.battery_detail_switch_summary);
+                mCurrentView.setClickable(true);
+                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.VISIBLE);
+                mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.VISIBLE);
+                mCurrentView.setOnClickListener(this);
+            }
+        }
+
+        @Override
+        public void onClick(View v) {
+            mBatteryController.setPowerSaveMode(!mPowerSave);
+        }
+
+        @Override
+        public Intent getSettingsIntent() {
+            return new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+        }
+
+        @Override
+        public void setToggleState(boolean state) {
+            // No toggle state.
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_BATTERY_DETAIL;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index cfc09a0..3750290 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -165,8 +165,8 @@
         private QSDetailItems mItems;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_bluetooth_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_bluetooth_label);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index a8e139c..de4c21c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -160,8 +160,8 @@
         private QSDetailItems mItems;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_cast_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_cast_title);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 6c7b337..c1dcfea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -223,8 +223,8 @@
     private final class CellularDetailAdapter implements DetailAdapter {
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_cellular_detail_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_cellular_detail_title);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index f73ee35..23a15b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -71,8 +71,6 @@
     protected void handleClick() {
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         mSetting.setValue(mState.value ? 0 : 1);
-        mEnable.setAllowAnimation(true);
-        mDisable.setAllowAnimation(true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index d96f735..39eda6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.view.LayoutInflater;
@@ -99,16 +100,6 @@
 
     @Override
     public void handleClick() {
-        if (mController.isVolumeRestricted()) {
-            // Collapse the panels, so the user can see the toast.
-            mHost.collapsePanels();
-            SysUIToast.makeText(mContext, mContext.getString(
-                    com.android.internal.R.string.error_message_change_not_allowed),
-                    Toast.LENGTH_LONG).show();
-            return;
-        }
-        mDisable.setAllowAnimation(true);
-        mDisableTotalSilence.setAllowAnimation(true);
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         if (mState.value) {
             mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
@@ -125,6 +116,8 @@
         final boolean newValue = zen != Global.ZEN_MODE_OFF;
         final boolean valueChanged = state.value != newValue;
         state.value = newValue;
+        state.disabledByPolicy = mController.isVolumeRestricted();
+        checkIfRestrictionEnforced(state, UserManager.DISALLOW_ADJUST_VOLUME);
         switch (zen) {
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
@@ -220,8 +213,8 @@
     private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_dnd_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_dnd_label);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 12c1298..39d9da1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -64,7 +64,7 @@
         }
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         boolean newState = !mState.value;
-        refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+        refreshState(newState);
         mFlashlightController.setFlashlight(newState);
     }
 
@@ -73,8 +73,8 @@
         // TODO: Flashlight available handling...
 //        state.visible = mFlashlightController.isAvailable();
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
-        if (arg instanceof UserBoolean) {
-            boolean value = ((UserBoolean) arg).value;
+        if (arg instanceof Boolean) {
+            boolean value = (Boolean) arg;
             if (value == state.value) {
                 return;
             }
@@ -83,7 +83,6 @@
             state.value = mFlashlightController.isEnabled();
         }
         final AnimationIcon icon = state.value ? mEnable : mDisable;
-        icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
         state.icon = icon;
         int onOrOffId = state.value
                 ? R.string.accessibility_quick_settings_flashlight_on
@@ -107,12 +106,12 @@
 
     @Override
     public void onFlashlightChanged(boolean enabled) {
-        refreshState(enabled ? UserBoolean.BACKGROUND_TRUE : UserBoolean.BACKGROUND_FALSE);
+        refreshState(enabled);
     }
 
     @Override
     public void onFlashlightError() {
-        refreshState(UserBoolean.BACKGROUND_FALSE);
+        refreshState(false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 250d567..55aa32b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -59,8 +59,6 @@
         final boolean isEnabled = (Boolean) mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
         mController.setHotspotEnabled(!isEnabled);
-        mEnable.setAllowAnimation(true);
-        mDisable.setAllowAnimation(true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 08540f6..e79aabf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -67,8 +67,6 @@
                     mHost.openPanels();
                     MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
                     mController.setLocationEnabled(!wasEnabled);
-                    mEnable.setAllowAnimation(true);
-                    mDisable.setAllowAnimation(true);
                 }
             });
             return;
@@ -76,8 +74,6 @@
         final boolean wasEnabled = (Boolean) mState.value;
         MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
         mController.setLocationEnabled(!wasEnabled);
-        mEnable.setAllowAnimation(true);
-        mDisable.setAllowAnimation(true);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index d85cf60..7bce54b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -63,15 +63,14 @@
         MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         final boolean newState = !mState.value;
         mController.setRotationLocked(newState);
-        refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+        refreshState(newState);
     }
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         if (mController == null) return;
-        final boolean rotationLocked = arg != null ? ((UserBoolean) arg).value
+        final boolean rotationLocked = arg != null ? (Boolean) arg
                 : mController.isRotationLocked();
-        final boolean userInitiated = arg != null ? ((UserBoolean) arg).userInitiated : false;
         // TODO: Handle accessibility rotation lock and whatnot.
 //        state.visible = mController.isRotationLockAffordanceVisible();
         if (state.value == rotationLocked && state.contentDescription != null) {
@@ -80,18 +79,15 @@
         }
         state.value = rotationLocked;
         final boolean portrait = isCurrentOrientationLockPortrait();
-        final AnimationIcon icon;
         if (rotationLocked) {
             final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label
                     : R.string.quick_settings_rotation_locked_landscape_label;
             state.label = mContext.getString(label);
-            icon = portrait ? mAutoToPortrait : mAutoToLandscape;
+            state.icon = portrait ? mAutoToPortrait : mAutoToLandscape;
         } else {
             state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
-            icon = portrait ? mPortraitToAuto : mLandscapeToAuto;
+            state.icon = portrait ? mPortraitToAuto : mLandscapeToAuto;
         }
-        icon.setAllowAnimation(userInitiated);
-        state.icon = icon;
         state.contentDescription = getAccessibilityString(rotationLocked,
                 R.string.accessibility_rotation_lock_on_portrait,
                 R.string.accessibility_rotation_lock_on_landscape,
@@ -145,8 +141,7 @@
     private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() {
         @Override
         public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
-            refreshState(rotationLocked ? UserBoolean.BACKGROUND_TRUE
-                    : UserBoolean.BACKGROUND_FALSE);
+            refreshState(rotationLocked);
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 21c5c96..167c611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -43,6 +43,7 @@
     private TextView mName;
     private Typeface mRegularTypeface;
     private Typeface mActivatedTypeface;
+    private View mRestrictedPadlock;
 
     public UserDetailItemView(Context context) {
         this(context, null);
@@ -59,6 +60,7 @@
     public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.UserDetailItemView, defStyleAttr, defStyleRes);
         final int N = a.getIndexCount();
@@ -95,6 +97,12 @@
         mAvatar.setDrawable(picture);
     }
 
+    public void setDisabledByAdmin(boolean disabled) {
+        mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
+        mName.setEnabled(!disabled);
+        mAvatar.setDisabled(disabled);
+    }
+
     @Override
     protected void onFinishInflate() {
         mAvatar = (UserAvatarView) findViewById(R.id.user_picture);
@@ -106,6 +114,7 @@
             mActivatedTypeface = mName.getTypeface();
         }
         updateTypeface();
+        mRestrictedPadlock = findViewById(R.id.restricted_padlock);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index d4f54b6..b44ef0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -16,17 +16,18 @@
 
 package com.android.systemui.qs.tiles;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
-import com.android.systemui.qs.PseudoGridView;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
 import android.content.Context;
+import android.content.Intent;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.R;
+import com.android.systemui.qs.PseudoGridView;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
 /**
  * Quick settings detail view for user switching.
  */
@@ -55,11 +56,13 @@
     public static class Adapter extends UserSwitcherController.BaseUserAdapter
             implements OnClickListener {
 
-        private Context mContext;
+        private final Context mContext;
+        private final UserSwitcherController mController;
 
         public Adapter(Context context, UserSwitcherController controller) {
             super(controller);
             mContext = context;
+            mController = controller;
         }
 
         @Override
@@ -77,6 +80,7 @@
                 v.bind(name, item.picture);
             }
             v.setActivated(item.isCurrent);
+            v.setDisabledByAdmin(item.isDisabledByAdmin);
             v.setTag(item);
             return v;
         }
@@ -85,8 +89,14 @@
         public void onClick(View view) {
             UserSwitcherController.UserRecord tag =
                     (UserSwitcherController.UserRecord) view.getTag();
-            MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER);
-            switchTo(tag);
+            if (tag.isDisabledByAdmin) {
+                final Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+                        mContext, tag.enforcedAdmin);
+                mController.startActivity(intent);
+            } else {
+                MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER);
+                switchTo(tag);
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 48b4096..95ea3f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -240,8 +240,8 @@
         private AccessPoint[] mAccessPoints;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_wifi_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_wifi_label);
         }
 
         public Intent getSettingsIntent() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 07915f8..255f29f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -93,12 +93,7 @@
     }
 
     private void refreshQuietModeState(boolean backgroundRefresh) {
-        if (backgroundRefresh) {
-            refreshState(isWorkModeEnabled() ? UserBoolean.BACKGROUND_TRUE
-                    : UserBoolean.BACKGROUND_FALSE);
-        } else {
-            refreshState(isWorkModeEnabled() ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
-        }
+        refreshState(isWorkModeEnabled());
     }
 
     @Override
@@ -108,28 +103,22 @@
             return;
         }
 
-        final boolean userInitialized;
-        if (arg instanceof UserBoolean) {
-            state.value = ((UserBoolean) arg).value;
-            userInitialized = ((UserBoolean) arg).userInitiated;
+        if (arg instanceof Boolean) {
+            state.value = (Boolean) arg;
         } else {
             state.value = isWorkModeEnabled();
-            userInitialized = false;
         }
 
-        final AnimationIcon icon;
         state.label = mContext.getString(R.string.quick_settings_work_mode_label);
         if (state.value) {
-            icon = mEnable;
+            state.icon = mEnable;
             state.contentDescription =  mContext.getString(
                     R.string.accessibility_quick_settings_work_mode_on);
         } else {
-            icon = mDisable;
+            state.icon = mDisable;
             state.contentDescription =  mContext.getString(
                     R.string.accessibility_quick_settings_work_mode_off);
         }
-        icon.setAllowAnimation(userInitialized);
-        state.icon = icon;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index b81c23a..2baefd5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
@@ -205,7 +206,7 @@
     public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -242,7 +243,7 @@
     public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -277,7 +278,7 @@
     public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -312,7 +313,7 @@
     public void preloadRecents() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -340,7 +341,7 @@
     public void cancelPreloadingRecents() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -365,11 +366,20 @@
     }
 
     @Override
-    public void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
-        mImpl.dockTopTask(draggingInRecents, stackCreateMode,initialBounds);
-        if (draggingInRecents) {
-            mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser();
+    public boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
+        // Ensure the device has been provisioned before allowing the user to interact with
+        // recents
+        if (!isUserSetup()) {
+            return false;
         }
+
+        if (mImpl.dockTopTask(draggingInRecents, stackCreateMode,initialBounds)) {
+            if (draggingInRecents) {
+                mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser();
+            }
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -422,7 +432,7 @@
     public void showNextAffiliatedTask() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -433,7 +443,7 @@
     public void showPrevAffiliatedTask() {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
-        if (!isDeviceProvisioned()) {
+        if (!isUserSetup()) {
             return;
         }
 
@@ -559,11 +569,12 @@
     }
 
     /**
-     * @return whether this device is provisioned.
+     * @return whether this device is provisioned and the current user is set up.
      */
-    private boolean isDeviceProvisioned() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+    private boolean isUserSetup() {
+        ContentResolver cr = mContext.getContentResolver();
+        return (Settings.Global.getInt(cr, Settings.Global.DEVICE_PROVISIONED, 0) != 0) &&
+                (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index ffcc805..db55f28 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -5,7 +5,7 @@
  * 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
+ * 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,
@@ -43,8 +43,10 @@
 import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
 import com.android.systemui.recents.events.activity.IterateRecentsEvent;
@@ -56,8 +58,7 @@
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
 import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
 import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
@@ -67,7 +68,6 @@
 import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
 import com.android.systemui.recents.history.RecentsHistoryView;
 import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
@@ -76,7 +76,7 @@
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.views.RecentsView;
 import com.android.systemui.recents.views.SystemBarScrimViews;
-import com.android.systemui.recents.views.ViewAnimation;
+import com.android.systemui.statusbar.BaseStatusBar;
 
 import java.util.ArrayList;
 
@@ -113,16 +113,12 @@
     private FinishRecentsRunnable mFinishLaunchHomeRunnable;
 
     // The trigger to automatically launch the current task
-    private DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
-        @Override
-        public void run() {
-            dismissRecentsToFocusedTask();
-        }
-    });
+    private int mFocusTimerDuration;
+    private DozeTrigger mIterateTrigger;
 
     /**
      * A common Runnable to finish Recents by launching Home with an animation depending on the
-     * last activity launch state.  Generally we always launch home when we exit Recents rather than
+     * last activity launch state. Generally we always launch home when we exit Recents rather than
      * just finishing the activity since we don't know what is behind Recents in the task stack.
      */
     class FinishRecentsRunnable implements Runnable {
@@ -196,13 +192,13 @@
         loader.loadTasks(this, plan, loadOpts);
 
         TaskStack stack = plan.getTaskStack();
+        ArrayList<Task> tasks = stack.getStackTasks();
+        int taskCount = stack.getTaskCount();
         mRecentsView.setTaskStack(stack);
 
         // Mark the task that is the launch target
         int launchTaskIndexInStack = 0;
         if (launchState.launchedToTaskId != -1) {
-            ArrayList<Task> tasks = stack.getStackTasks();
-            int taskCount = tasks.size();
             for (int j = 0; j < taskCount; j++) {
                 Task t = tasks.get(j);
                 if (t.key.id == launchState.launchedToTaskId) {
@@ -214,12 +210,12 @@
         }
 
         // Animate the SystemUI scrims into view
-        boolean hasStatusBarScrim = stack.getStackTaskCount() > 0;
+        boolean hasStatusBarScrim = taskCount > 0;
         boolean animateStatusBarScrim = launchState.launchedFromHome;
-        boolean hasNavBarScrim = (stack.getStackTaskCount() > 0) && !config.hasTransposedNavBar;
+        boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
         boolean animateNavBarScrim = true;
-        mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim, hasNavBarScrim,
-                animateNavBarScrim);
+        mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim,
+                hasNavBarScrim, animateNavBarScrim);
 
         // Keep track of whether we launched from the nav bar button or via alt-tab
         if (launchState.launchedWithAltTab) {
@@ -236,7 +232,6 @@
             MetricsLogger.count(this, "overview_source_home", 1);
         }
         // Keep track of the total stack task count
-        int taskCount = stack.getStackTaskCount();
         MetricsLogger.histogram(this, "overview_task_count", taskCount);
     }
 
@@ -273,7 +268,7 @@
             // If we have a focused Task, launch that Task now
             if (mRecentsView.launchPreviousTask()) return true;
             // If none of the other cases apply, then just go Home
-            dismissRecentsToHome(true);
+            dismissRecentsToHome(true /* animateTaskViews */);
         }
         return false;
     }
@@ -287,7 +282,7 @@
             // If we have a focused Task, launch that Task now
             if (mRecentsView.launchFocusedTask()) return true;
             // If none of the other cases apply, then just go Home
-            dismissRecentsToHome(true);
+            dismissRecentsToHome(true /* animateTaskViews */);
             return true;
         }
         return false;
@@ -296,21 +291,18 @@
     /**
      * Dismisses Recents directly to Home without checking whether it is currently visible.
      */
-    void dismissRecentsToHome(boolean animated) {
-        if (animated) {
-            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(null,
-                    mFinishLaunchHomeRunnable, null);
-            mRecentsView.startExitToHomeAnimation(
-                    new ViewAnimation.TaskViewExitContext(exitTrigger));
-        } else {
-            mFinishLaunchHomeRunnable.run();
-        }
-    }
-
-    /** Dismisses Recents directly to Home without transition animation. */
-    void dismissRecentsToHomeWithoutTransitionAnimation() {
-        finish();
-        overridePendingTransition(0, 0);
+    void dismissRecentsToHome(boolean animateTaskViews) {
+        DismissRecentsToHomeAnimationStarted dismissEvent =
+                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
+        dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
+        dismissEvent.addPostAnimationCallback(new Runnable() {
+            @Override
+            public void run() {
+                Recents.getSystemServices().sendCloseSystemWindows(
+                        BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
+            }
+        });
+        EventBus.getDefault().send(dismissEvent);
     }
 
     /** Dismisses Recents directly to Home if we currently aren't transitioning. */
@@ -343,7 +335,7 @@
         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
 
         // Initialize the widget host (the host id is static and does not change)
-        if (!RecentsDebugFlags.Static.DisableSearchBar) {
+        if (RecentsDebugFlags.Static.EnableSearchBar) {
             mAppWidgetHost = new RecentsAppWidgetHost(this, RecentsAppWidgetHost.HOST_ID);
         }
         mPackageMonitor = new RecentsPackageMonitor();
@@ -360,6 +352,14 @@
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
 
+        mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
+        mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
+            @Override
+            public void run() {
+                dismissRecentsToFocusedTask();
+            }
+        });
+
         // Create the home intent runnable
         Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
         homeIntent.addCategory(Intent.CATEGORY_HOME);
@@ -368,14 +368,14 @@
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
 
         // Bind the search app widget when we first start up
-        if (!RecentsDebugFlags.Static.DisableSearchBar) {
+        if (RecentsDebugFlags.Static.EnableSearchBar) {
             mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
         }
 
         // Register the broadcast receiver to handle messages when the screen is turned off
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
-        if (!RecentsDebugFlags.Static.DisableSearchBar) {
+        if (RecentsDebugFlags.Static.EnableSearchBar) {
             filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
         }
         registerReceiver(mSystemBroadcastReceiver, filter);
@@ -475,7 +475,7 @@
         mPackageMonitor.unregister();
 
         // Stop listening for widget package changes if there was one bound
-        if (!RecentsDebugFlags.Static.DisableSearchBar) {
+        if (RecentsDebugFlags.Static.EnableSearchBar) {
             mAppWidgetHost.stopListening();
         }
 
@@ -546,7 +546,8 @@
                     if (backward) {
                         EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
                     } else {
-                        EventBus.getDefault().send(new FocusNextTaskViewEvent());
+                        EventBus.getDefault().send(
+                                new FocusNextTaskViewEvent(false /* showTimerIndicator */));
                     }
                     mLastTabKeyEventTime = SystemClock.elapsedRealtime();
 
@@ -558,7 +559,8 @@
                 return true;
             }
             case KeyEvent.KEYCODE_DPAD_UP: {
-                EventBus.getDefault().send(new FocusNextTaskViewEvent());
+                EventBus.getDefault().send(
+                        new FocusNextTaskViewEvent(false /* showTimerIndicator */));
                 return true;
             }
             case KeyEvent.KEYCODE_DPAD_DOWN: {
@@ -567,12 +569,14 @@
             }
             case KeyEvent.KEYCODE_DEL:
             case KeyEvent.KEYCODE_FORWARD_DEL: {
-                EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
+                if (event.getRepeatCount() <= 0) {
+                    EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
 
-                // Keep track of deletions by keyboard
-                MetricsLogger.histogram(this, "overview_task_dismissed_source",
-                        Constants.Metrics.DismissSourceKeyboard);
-                return true;
+                    // Keep track of deletions by keyboard
+                    MetricsLogger.histogram(this, "overview_task_dismissed_source",
+                            Constants.Metrics.DismissSourceKeyboard);
+                    return true;
+                }
             }
             default:
                 break;
@@ -582,7 +586,10 @@
 
     @Override
     public void onUserInteraction() {
-        EventBus.getDefault().send(new UserInteractionEvent());
+        // TODO: Prevent creating so many events here
+        final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+        EventBus.getDefault().send(new UserInteractionEvent(debugFlags.isFastToggleRecentsEnabled()
+                && debugFlags.isFastToggleIndicatorEnabled()));
     }
 
     @Override
@@ -597,7 +604,7 @@
         if (!dismissHistory()) {
             RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
             if (launchState.launchedFromHome) {
-                dismissRecentsToHome(true);
+                dismissRecentsToHome(true /* animateTaskViews */);
             } else {
                 dismissRecentsToLaunchTargetTaskOrHome();
             }
@@ -606,11 +613,14 @@
 
     public final void onBusEvent(IterateRecentsEvent event) {
         if (!dismissHistory()) {
+            final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+
             // Focus the next task
-            EventBus.getDefault().send(new FocusNextTaskViewEvent());
+            EventBus.getDefault().send(
+                    new FocusNextTaskViewEvent(debugFlags.isFastToggleRecentsEnabled()
+                            && debugFlags.isFastToggleIndicatorEnabled()));
 
             // Start dozing after the recents button is clicked
-            RecentsDebugFlags debugFlags = Recents.getDebugFlags();
             if (debugFlags.isFastToggleRecentsEnabled()) {
                 if (!mIterateTrigger.isDozing()) {
                     mIterateTrigger.startDozing();
@@ -618,6 +628,8 @@
                     mIterateTrigger.poke();
                 }
             }
+
+            MetricsLogger.action(this, MetricsLogger.ACTION_OVERVIEW_PAGE);
         }
     }
 
@@ -638,13 +650,13 @@
                 hideEvent.addPostAnimationCallback(new Runnable() {
                     @Override
                     public void run() {
-                        dismissRecentsToHome(true /* animated */);
+                        dismissRecentsToHome(true /* animateTaskViews */);
                     }
                 });
                 EventBus.getDefault().send(hideEvent);
 
             } else {
-                dismissRecentsToHome(true /* animated */);
+                dismissRecentsToHome(true /* animateTaskViews */);
             }
         } else {
             // Do nothing
@@ -653,12 +665,9 @@
 
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
         // Try and start the enter animation (or restart it on configuration changed)
-        ReferenceCountedTrigger t = new ReferenceCountedTrigger();
-        ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
-        ctx.postAnimationTrigger.increment();
-        if (mSearchWidgetInfo != null) {
-            if (!RecentsDebugFlags.Static.DisableSearchBar) {
-                ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+        if (RecentsDebugFlags.Static.EnableSearchBar) {
+            if (mSearchWidgetInfo != null) {
+                event.addPostAnimationCallback(new Runnable() {
                     @Override
                     public void run() {
                         // Start listening for widget package changes if there is one bound
@@ -669,8 +678,6 @@
                 });
             }
         }
-        mRecentsView.startEnterRecentsAnimation(ctx);
-        ctx.postAnimationTrigger.decrement();
     }
 
     public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
@@ -709,13 +716,13 @@
         intent.setComponent(intent.resolveActivity(getPackageManager()));
         TaskStackBuilder.create(this)
                 .addNextIntentWithParentStack(intent).startActivities(null,
-                new UserHandle(event.task.key.userId));
+                        new UserHandle(event.task.key.userId));
 
         // Keep track of app-info invocations
         MetricsLogger.count(this, "overview_app_info", 1);
     }
 
-    public final void onBusEvent(DismissTaskEvent event) {
+    public final void onBusEvent(DeleteTaskDataEvent event) {
         // Remove any stored data from the loader
         RecentsTaskLoader loader = Recents.getTaskLoader();
         loader.deleteTaskData(event.task, false);
@@ -731,7 +738,7 @@
             mRecentsView.showEmptyView();
         } else {
             // Just go straight home (no animation necessary because there are no more task views)
-            dismissRecentsToHome(false /* animated */);
+            dismissRecentsToHome(false /* animateTaskViews */);
         }
 
         // Keep track of all-deletions
@@ -744,7 +751,7 @@
 
     public final void onBusEvent(LaunchTaskFailedEvent event) {
         // Return to Home
-        dismissRecentsToHome(true);
+        dismissRecentsToHome(true /* animateTaskViews */);
 
         MetricsLogger.count(this, "overview_task_launch_failed", 1);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 7547570f..61780f8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.provider.Settings;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
@@ -74,9 +73,6 @@
     public int svelteLevel;
     public int searchBarSpaceHeightPx;
 
-    /** Dev options and global settings */
-    public boolean lockToAppEnabled;
-
     public RecentsConfiguration(Context context) {
         // Load only resources that can not change after the first load either through developer
         // settings or via multi window
@@ -106,12 +102,7 @@
     /**
      * Updates the configuration based on the current state of the system
      */
-    void update(Context context, SystemServicesProxy ssp, Rect windowRect) {
-        // Only update resources that can change after the first load, either through developer
-        // settings or via multi window
-        lockToAppEnabled = !ssp.hasFreeformWorkspaceSupport() &&
-                ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0;
-
+    void update(Rect windowRect) {
         // Recompute some values based on the given state, since we can not rely on the resource
         // system to get certain values.
         boolean isLandscape = windowRect.width() > windowRect.height();
@@ -148,7 +139,7 @@
         } else {
             // In portrait, the search bar appears on the top (which already has the inset)
             int top = searchBarBounds.isEmpty() ? topInset : 0;
-            taskStackBounds.set(windowBounds.left, searchBarBounds.bottom + top,
+            taskStackBounds.set(windowBounds.left, windowBounds.top + searchBarBounds.bottom + top,
                     windowBounds.right - rightInset, windowBounds.bottom);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index 40c84ba..3151fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -27,13 +27,14 @@
 public class RecentsDebugFlags implements TunerService.Tunable {
 
     private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
+    private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator";
     private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
 
     public static class Static {
         // Enables debug drawing for the transition thumbnail
         public static final boolean EnableTransitionThumbnailDebugMode = false;
-        // This disables the search bar integration
-        public static final boolean DisableSearchBar = true;
+        // This enables the search bar integration
+        public static final boolean EnableSearchBar = false;
         // This disables the bitmap and icon caches
         public static final boolean DisableBackgroundCache = false;
         // Enables the simulated task affiliations
@@ -49,6 +50,7 @@
     }
 
     private boolean mFastToggleRecents;
+    private boolean mFastToggleIndicator;
     private boolean mInitialStatePaging;
 
     /**
@@ -58,7 +60,8 @@
     public RecentsDebugFlags(Context context) {
         // Register all our flags, this will also call onTuningChanged() for each key, which will
         // initialize the current state of each flag
-        TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_INITIAL_STATE_PAGING);
+        TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_FAST_TOGGLE_INDICATOR,
+                KEY_INITIAL_STATE_PAGING);
     }
 
     /**
@@ -69,6 +72,13 @@
     }
 
     /**
+     * @return whether we are enabling the fast toggle indicator.
+     */
+    public boolean isFastToggleIndicatorEnabled() {
+        return mFastToggleIndicator;
+    }
+
+    /**
      * @return whether the initial stack state is paging.
      */
     public boolean isInitialStatePaging() {
@@ -82,6 +92,10 @@
                 mFastToggleRecents = (newValue != null) &&
                         (Integer.parseInt(newValue) != 0);
                 break;
+            case KEY_FAST_TOGGLE_INDICATOR:
+                mFastToggleIndicator = (newValue != null) &&
+                        (Integer.parseInt(newValue) != 0);
+                break;
             case KEY_INITIAL_STATE_PAGING:
                 mInitialStatePaging = (newValue != null) &&
                         (Integer.parseInt(newValue) != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 949fb86..7c25d24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ITaskStackListener;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -60,6 +61,7 @@
 import com.android.systemui.recents.views.TaskStackView;
 import com.android.systemui.recents.views.TaskViewHeader;
 import com.android.systemui.recents.views.TaskViewTransform;
+import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.ArrayList;
@@ -74,8 +76,6 @@
         ActivityOptions.OnAnimationFinishedListener {
 
     private final static String TAG = "RecentsImpl";
-    private final static boolean DEBUG = false;
-
     // The minimum amount of time between each recents button press that we will handle
     private final static int MIN_TOGGLE_DELAY_MS = 350;
     // The duration within which the user releasing the alt tab (from when they pressed alt tab)
@@ -104,11 +104,13 @@
             mHandler.post(this);
         }
 
+        @Override
+        public void onActivityPinned() {
+        }
+
         /** Preloads the next task */
         public void run() {
-            // TODO: Temporarily skip this if multi stack is enabled
-            /*
-            RecentsConfiguration config = RecentsConfiguration.getInstance();
+            RecentsConfiguration config = Recents.getConfiguration();
             if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
                 RecentsTaskLoader loader = Recents.getTaskLoader();
                 SystemServicesProxy ssp = Recents.getSystemServices();
@@ -130,7 +132,6 @@
                 launchOpts.onlyLoadPausedActivities = true;
                 loader.loadTasks(mContext, plan, launchOpts);
             }
-            */
         }
     }
 
@@ -180,8 +181,6 @@
         mContext = context;
         mHandler = new Handler();
         mAppWidgetHost = new RecentsAppWidgetHost(mContext, RecentsAppWidgetHost.HOST_ID);
-        Resources res = mContext.getResources();
-        LayoutInflater inflater = LayoutInflater.from(mContext);
 
         // Initialize the static foreground thread
         ForegroundThread.get();
@@ -192,14 +191,8 @@
         ssp.registerTaskStackListener(mTaskStackListener);
 
         // Initialize the static configuration resources
-        mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-        mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-        mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
-        mDummyStackView = new TaskStackView(mContext, new TaskStack());
-        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
-                null, false);
-        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
+        reloadHeaderBarLayout();
+        updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
 
         // When we start, preload the data associated with the previous recent tasks.
         // We can use a new plan since the caches will be the same.
@@ -215,11 +208,12 @@
 
     public void onBootCompleted() {
         mBootCompleted = true;
-        reloadHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
+        updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
     }
 
-    @Override
     public void onConfigurationChanged() {
+        reloadHeaderBarLayout();
+        updateHeaderBarLayout(true /* tryAndBindSearchWidget */, null /* stack */);
         // Don't reuse task stack views if the configuration changes
         mCanReuseTaskStackViews = false;
         Recents.getConfiguration().updateOnConfigurationChange();
@@ -251,7 +245,6 @@
         }
     }
 
-    @Override
     public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
             boolean animate, boolean reloadTasks) {
         mTriggeredFromAltTab = triggeredFromAltTab;
@@ -294,7 +287,6 @@
         }
     }
 
-    @Override
     public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         if (mBootCompleted) {
             if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
@@ -315,7 +307,6 @@
         }
     }
 
-    @Override
     public void toggleRecents() {
         // Skip this toggle if we are already waiting to trigger recents via alt-tab
         if (mFastAltTabTrigger.isDozing()) {
@@ -332,7 +323,6 @@
             if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
                 RecentsConfiguration config = Recents.getConfiguration();
                 RecentsActivityLaunchState launchState = config.getLaunchState();
-                RecentsDebugFlags flags = Recents.getDebugFlags();
                 if (!launchState.launchedWithAltTab) {
                     // Notify recents to move onto the next task
                     EventBus.getDefault().post(new IterateRecentsEvent());
@@ -360,6 +350,9 @@
 
                 // Otherwise, start the recents activity
                 startRecentsActivity(topTask, isTopTaskHome.value, true /* animate */);
+
+                // Only close the other system windows if we are actually showing recents
+                ssp.sendCloseSystemWindows(BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS);
                 mLastToggleTime = SystemClock.elapsedRealtime();
             }
         } catch (ActivityNotFoundException e) {
@@ -367,7 +360,6 @@
         }
     }
 
-    @Override
     public void preloadRecents() {
         // Preload only the raw task list into a new load plan (which will be consumed by the
         // RecentsActivity) only if there is a task to animate to.
@@ -380,7 +372,7 @@
             sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
             loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
             TaskStack stack = sInstanceLoadPlan.getTaskStack();
-            if (stack.getStackTaskCount() > 0) {
+            if (stack.getTaskCount() > 0) {
                 // We try and draw the thumbnail transition bitmap in parallel before
                 // toggle/show recents is called
                 preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
@@ -388,17 +380,14 @@
         }
     }
 
-    @Override
     public void cancelPreloadingRecents() {
         // Do nothing
     }
 
-    @Override
     public void onDraggingInRecents(float distanceFromTop) {
         EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
     }
 
-    @Override
     public void onDraggingInRecentsEnded(float velocity) {
         EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
     }
@@ -414,7 +403,7 @@
         TaskStack focusedStack = plan.getTaskStack();
 
         // Return early if there are no tasks in the focused stack
-        if (focusedStack == null || focusedStack.getStackTaskCount() == 0) return;
+        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
 
         ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
         // Return early if there is no running task
@@ -466,7 +455,7 @@
         TaskStack focusedStack = plan.getTaskStack();
 
         // Return early if there are no tasks in the focused stack
-        if (focusedStack == null || focusedStack.getStackTaskCount() == 0) return;
+        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
 
         ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
         // Return early if there is no running task (can't determine affiliated tasks in this case)
@@ -539,14 +528,18 @@
         showRelativeAffiliatedTask(false);
     }
 
-    public void dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
+    public boolean dockTopTask(boolean draggingInRecents, int stackCreateMode, Rect initialBounds) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
-        if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) {
+        boolean screenPinningActive = ssp.isScreenPinningActive();
+        boolean isTopTaskHome = SystemServicesProxy.isHomeStack(topTask.stackId);
+        if (topTask != null && !isTopTaskHome && !screenPinningActive) {
             ssp.moveTaskToDockedStack(topTask.id, stackCreateMode, initialBounds);
             showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */,
                     true /* reloadTasks*/);
+            return true;
         }
+        return false;
     }
 
     /**
@@ -559,6 +552,26 @@
     }
 
     /**
+     * Reloads all the layouts for the header bar transition.
+     */
+    private void reloadHeaderBarLayout() {
+        Resources res = mContext.getResources();
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+
+        mStatusBarHeight = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mNavBarHeight = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height);
+        mNavBarWidth = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_width);
+        mTaskBarHeight = res.getDimensionPixelSize(
+                R.dimen.recents_task_bar_height);
+        mDummyStackView = new TaskStackView(mContext, new TaskStack());
+        mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+                null, false);
+    }
+
+    /**
      * Prepares the header bar layout for the next transition, if the task view bounds has changed
      * since the last call, it will attempt to re-measure and layout the header bar to the new size.
      *
@@ -566,15 +579,16 @@
      *                               is not already bound (can be expensive)
      * @param stack the stack to initialize the stack layout with
      */
-    private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
+    private void updateHeaderBarLayout(boolean tryAndBindSearchWidget,
+            TaskStack stack) {
         RecentsConfiguration config = Recents.getConfiguration();
         SystemServicesProxy ssp = Recents.getSystemServices();
         Rect windowRect = ssp.getWindowRect();
 
         // Update the configuration for the current state
-        config.update(mContext, ssp, ssp.getWindowRect());
+        config.update(windowRect);
 
-        if (!RecentsDebugFlags.Static.DisableSearchBar && tryAndBindSearchWidget) {
+        if (RecentsDebugFlags.Static.EnableSearchBar && tryAndBindSearchWidget) {
             // Try and pre-emptively bind the search widget on startup to ensure that we
             // have the right thumbnail bounds to animate to.
             // Note: We have to reload the widget id before we get the task stack bounds below
@@ -631,7 +645,7 @@
         preloadIcon(topTask);
 
         // Update the header bar if necessary
-        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
 
         // Update the destination rect
         mDummyStackView.updateLayoutForStack(stack);
@@ -817,7 +831,7 @@
         TaskStack stack = sInstanceLoadPlan.getTaskStack();
 
         // Update the header bar if necessary
-        reloadHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
+        updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
 
         // Prepare the dummy stack for the transition
         mDummyStackView.updateLayoutForStack(stack);
@@ -831,7 +845,7 @@
             return;
         }
 
-        boolean hasRecentTasks = stack.getStackTaskCount() > 0;
+        boolean hasRecentTasks = stack.getTaskCount() > 0;
         boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
 
         if (useThumbnailTransition) {
@@ -850,11 +864,19 @@
         if (!useThumbnailTransition) {
             // If there is no thumbnail transition, but is launching from home into recents, then
             // use a quick home transition and do the animation from home
-            if (!RecentsDebugFlags.Static.DisableSearchBar && hasRecentTasks) {
+            if (hasRecentTasks) {
                 SystemServicesProxy ssp = Recents.getSystemServices();
                 String homeActivityPackage = ssp.getHomeActivityPackageName();
-                String searchWidgetPackage = Prefs.getString(mContext,
-                        Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
+                String searchWidgetPackage = null;
+                if (RecentsDebugFlags.Static.EnableSearchBar) {
+                    searchWidgetPackage = Prefs.getString(mContext,
+                            Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null);
+                } else {
+                    AppWidgetProviderInfo searchWidgetInfo = ssp.resolveSearchAppWidget();
+                    if (searchWidgetInfo != null) {
+                        searchWidgetPackage = searchWidgetInfo.provider.getPackageName();
+                    }
+                }
 
                 // Determine whether we are coming from a search owned home activity
                 boolean fromSearchHome = (homeActivityPackage != null) &&
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index 5c49ac3..212c7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -27,7 +27,6 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.MutableBoolean;
-
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
 import java.lang.ref.WeakReference;
@@ -291,6 +290,28 @@
     }
 
     /**
+     * An event that can be reusable, only used for situations where we want to reduce memory
+     * allocations when events are sent frequently (ie. on scroll).
+     */
+    public static class ReusableEvent extends Event {
+
+        private int mDispatchCount;
+
+        protected ReusableEvent() {}
+
+        @Override
+        void onPostDispatch() {
+            super.onPostDispatch();
+            mDispatchCount++;
+        }
+
+        @Override
+        protected Object clone() throws CloneNotSupportedException {
+            throw new CloneNotSupportedException();
+        }
+    }
+
+    /**
      * An inter-process event super class that allows us to track user state across subscriber
      * invocations.
      */
@@ -663,8 +684,6 @@
 
     /**
      * Registers a new subscriber.
-     *
-     * @return return whether or not this
      */
     private void registerSubscriber(Object subscriber, int priority,
             MutableBoolean hasInterprocessEventsChangedOut) {
@@ -773,8 +792,11 @@
         event.onPreDispatch();
 
         // We need to clone the list in case a subscriber unregisters itself during traversal
+        // TODO: Investigate whether we can skip the object creation here
         eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
-        for (final EventHandler eventHandler : eventHandlers) {
+        int eventHandlerCount = eventHandlers.size();
+        for (int i = 0; i < eventHandlerCount; i++) {
+            final EventHandler eventHandler = eventHandlers.get(i);
             if (eventHandler.subscriber.getReference() != null) {
                 if (event.requiresPost) {
                     mHandler.post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
index 5f3e830..e7be858 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
@@ -21,6 +21,11 @@
 /**
  * This is sent when the task animation when dismissing Recents starts.
  */
-public class DismissRecentsToHomeAnimationStarted extends EventBus.Event {
-    // Simple event
+public class DismissRecentsToHomeAnimationStarted extends EventBus.AnimatedEvent {
+
+    public final boolean animated;
+
+    public DismissRecentsToHomeAnimationStarted(boolean animated) {
+        this.animated = animated;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
index b31f320..918875a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
@@ -23,6 +23,6 @@
  * we can start in-app animations so that they don't conflict with the window transition into
  * Recents.
  */
-public class EnterRecentsWindowAnimationCompletedEvent extends EventBus.Event {
+public class EnterRecentsWindowAnimationCompletedEvent extends EventBus.AnimatedEvent {
     // Simple event
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
index 8ae8c53..fa806eb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recents;
+package com.android.systemui.recents.events.activity;
 
 import com.android.systemui.recents.events.EventBus;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
index e85dea3..af3eeb0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents.events.activity;
 
 import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
 /**
  * This is sent when the history view will be closed.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
index 457d81e..21b9301 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
@@ -22,7 +22,7 @@
 import com.android.systemui.recents.views.TaskView;
 
 /**
- * This is sent to launch a task from Recents.
+ * This event is sent to request that a particular task is launched.
  */
 public class LaunchTaskEvent extends EventBus.Event {
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
new file mode 100644
index 0000000..3925ab1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent following {@link LaunchTaskEvent} after the call to the system is made to
+ * start the task.
+ */
+public class LaunchTaskStartedEvent extends EventBus.AnimatedEvent {
+
+    public final TaskView taskView;
+    public final boolean screenPinningRequested;
+
+    public LaunchTaskStartedEvent(TaskView taskView, boolean screenPinningRequested) {
+        this.taskView = taskView;
+        this.screenPinningRequested = screenPinningRequested;
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
index 94e5a97..b39d645 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents.events.activity;
 
 import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
 /**
  * This is sent when the history view button is clicked.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
index b94ed7b..7579cd8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.recents.events.activity;
 
-import com.android.systemui.recents.RecentsAppWidgetHost;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.model.TaskStack;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
index bcbbde8..4ed0270 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
@@ -18,16 +18,16 @@
 
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.views.TaskView;
 
 /**
- * This is sent when a {@link Task} has been dismissed.
+ * This is sent when the data associated with a given {@link Task} should be deleted from the
+ * system.
  */
-public class DismissTaskEvent extends EventBus.Event {
+public class DeleteTaskDataEvent extends EventBus.Event {
 
     public final Task task;
 
-    public DismissTaskEvent(Task task) {
+    public DeleteTaskDataEvent(Task task) {
         this.task = task;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
index 968890a..1165f4e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
@@ -21,15 +21,15 @@
 import com.android.systemui.recents.views.TaskView;
 
 /**
- * This is sent when a {@link TaskView} has been dismissed.
+ * This event is sent to request that the given {@link TaskView} is dismissed.
  */
-public class DismissTaskViewEvent extends EventBus.Event {
+public class DismissTaskViewEvent extends EventBus.AnimatedEvent {
 
-    public final Task task;
     public final TaskView taskView;
+    public final Task task;
 
-    public DismissTaskViewEvent(Task task, TaskView taskView) {
-        this.task = task;
+    public DismissTaskViewEvent(TaskView taskView, Task task) {
         this.taskView = taskView;
+        this.task = task;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
index cb5011a..ad9feb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
@@ -16,16 +16,21 @@
 
 package com.android.systemui.recents.events.ui;
 
+import android.util.MutableInt;
 import com.android.systemui.recents.events.EventBus;
 
 /**
  * This is sent whenever a new scroll gesture happens on a stack view.
  */
-public class StackViewScrolledEvent extends EventBus.Event {
+public class StackViewScrolledEvent extends EventBus.ReusableEvent {
 
-    public final int yMovement;
+    public final MutableInt yMovement;
 
-    public StackViewScrolledEvent(int yMovement) {
-        this.yMovement = yMovement;
+    public StackViewScrolledEvent() {
+        yMovement = new MutableInt(0);
+    }
+
+    public void updateY(int y) {
+        yMovement.value = y;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
similarity index 75%
copy from packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
index bcbbde8..7bd0958 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
@@ -21,13 +21,15 @@
 import com.android.systemui.recents.views.TaskView;
 
 /**
- * This is sent when a {@link Task} has been dismissed.
+ * This event is sent when a {@link TaskView} has been dismissed and is no longer visible.
  */
-public class DismissTaskEvent extends EventBus.Event {
+public class TaskViewDismissedEvent extends EventBus.Event {
 
     public final Task task;
+    public final TaskView taskView;
 
-    public DismissTaskEvent(Task task) {
+    public TaskViewDismissedEvent(Task task, TaskView taskView) {
         this.task = task;
+        this.taskView = taskView;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
index 6e6cd84..5a132c2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
@@ -22,5 +22,10 @@
  * This is sent whenever the user interacts with the activity.
  */
 public class UserInteractionEvent extends EventBus.Event {
-    // Simple event
+
+    public final boolean showTimerIndicator;
+
+    public UserInteractionEvent(boolean showTimerIndicator) {
+        this.showTimerIndicator = showTimerIndicator;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
index 21321f2..b85ddac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
@@ -25,6 +25,7 @@
  */
 public class DragDropTargetChangedEvent extends EventBus.Event {
 
+    // The task that is currently being dragged
     public final Task task;
     public final DropTarget dropTarget;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
index 8aa4631..73c282f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents.events.ui.dragndrop;
 
 import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.views.DropTarget;
 import com.android.systemui.recents.views.TaskView;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
index 9f3e9d5..df74018 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
@@ -17,9 +17,10 @@
 package com.android.systemui.recents.events.ui.focus;
 
 import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.views.TaskView;
 
 /**
- * Dismisses the currently focused task view.
+ * This event is sent to request that the currently focused {@link TaskView} is dismissed.
  */
 public class DismissFocusedTaskViewEvent extends EventBus.Event {
     // Simple event
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
index 171ab5e..def4ae1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
@@ -22,5 +22,10 @@
  * Focuses the next task view in the stack.
  */
 public class FocusNextTaskViewEvent extends EventBus.Event {
-    // Simple event
+
+    public final boolean showTimerIndicator;
+
+    public FocusNextTaskViewEvent(boolean showTimerIndicator) {
+        this.showTimerIndicator = showTimerIndicator;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 72ec7b7..80597bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -26,6 +26,7 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.events.EventBus;
@@ -129,6 +130,9 @@
             SystemServicesProxy ssp = Recents.getSystemServices();
             ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
                     ActivityOptions.makeBasic());
+
+            MetricsLogger.action(v.getContext(), MetricsLogger.ACTION_OVERVIEW_SELECT,
+                    task.key.getComponent().toString());
         }
 
         @Override
@@ -185,7 +189,6 @@
      * remove the task from the TaskStack since the TaskStackView will also receive this event.
      */
     public void removeTasks(String packageName, int userId) {
-        boolean packagesRemoved = false;
         for (int i = mRows.size() - 1; i >= 0; i--) {
             Row row = mRows.get(i);
             if (row.getViewType() == TASK_ROW_VIEW_TYPE) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
index e0a2730..a91ea7e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
@@ -22,7 +22,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 
 
 /**
@@ -65,7 +65,7 @@
             RecentsHistoryAdapter.TaskRow taskRow = (RecentsHistoryAdapter.TaskRow) row;
 
             // Remove the task from the system
-            EventBus.getDefault().send(new DismissTaskEvent(taskRow.task));
+            EventBus.getDefault().send(new DeleteTaskDataEvent(taskRow.task));
             mAdapter.onTaskRemoved(taskRow.task, position);
 
             // Keep track of deletions by swiping within history
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
index 9524da5..39bb6ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
@@ -28,6 +28,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsActivity;
@@ -35,7 +36,6 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.TaskStack;
 
 /**
@@ -100,6 +100,8 @@
         });
         mAdapter.updateTasks(getContext(), stack);
         mIsVisible = true;
+
+        MetricsLogger.visible(mRecyclerView.getContext(), MetricsLogger.OVERVIEW_HISTORY);
     }
 
     /**
@@ -130,6 +132,8 @@
             setVisibility(View.INVISIBLE);
         }
         mIsVisible = false;
+
+        MetricsLogger.hidden(mRecyclerView.getContext(), MetricsLogger.OVERVIEW_HISTORY);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index 367f2e2..2637d88 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -18,8 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.Log;
 
 import java.util.ArrayList;
 
@@ -30,8 +28,8 @@
 public class ReferenceCountedTrigger {
 
     int mCount;
-    ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
-    ArrayList<Runnable> mLastDecRunnables = new ArrayList<Runnable>();
+    ArrayList<Runnable> mFirstIncRunnables = new ArrayList<>();
+    ArrayList<Runnable> mLastDecRunnables = new ArrayList<>();
     Runnable mErrorRunnable;
 
     // Convenience runnables
@@ -107,16 +105,20 @@
         mLastDecRunnables.clear();
     }
 
-    /** Convenience method to decrement this trigger as a runnable. */
-    public Runnable decrementAsRunnable() {
-        return mDecrementRunnable;
-    }
-    /** Convenience method to decrement this trigger as a animator listener. */
+    /**
+     * Convenience method to decrement this trigger as a animator listener.  This listener is
+     * guarded to prevent being called back multiple times, and will trigger a decrement once and
+     * only once.
+     */
     public Animator.AnimatorListener decrementOnAnimationEnd() {
         return new AnimatorListenerAdapter() {
+            private boolean hasEnded;
+
             @Override
             public void onAnimationEnd(Animator animation) {
+                if (hasEnded) return;
                 decrement();
+                hasEnded = true;
             }
         };
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 35e53f6..01de60c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -58,8 +58,9 @@
 import android.util.MutableBoolean;
 import android.util.Pair;
 import android.view.Display;
-import android.view.IDockDividerVisibilityListener;
+import android.view.IDockedStackListener;
 import android.view.WindowManager;
+import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
 import com.android.internal.app.AssistUtils;
@@ -78,7 +79,7 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 
 /**
  * Acts as a shim around the real system services that we need to access data from, and provides
@@ -128,7 +129,9 @@
         mDisplay = mWm.getDefaultDisplay();
         mRecentsPackage = context.getPackageName();
         mHasFreeformWorkspaceSupport =
-                mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+                mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
+                        Settings.Global.getInt(context.getContentResolver(),
+                                DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
 
         // Get the dummy thumbnail width/heights
         Resources res = context.getResources();
@@ -304,13 +307,15 @@
     }
 
     /** Docks a task to the side of the screen and starts it. */
-    public void startTaskInDockedMode(int taskId, int createMode) {
+    public void startTaskInDockedMode(Context context, int taskId, int createMode) {
         if (mIam == null) return;
 
         try {
-            final ActivityOptions options = ActivityOptions.makeBasic();
+            // TODO: Determine what animation we want for the incoming task
+            final ActivityOptions options = ActivityOptions.makeCustomAnimation(context, 0, 0);
             options.setDockCreateMode(createMode);
-            mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, options.toBundle());
+            options.setLaunchStackId(DOCKED_STACK_ID);
+            mIam.startActivityFromRecents(taskId, options.toBundle());
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -362,7 +367,7 @@
     }
 
     /**
-     * @return whether there are any docked tasks.
+     * @return whether there are any docked tasks for the current user.
      */
     public boolean hasDockedTask() {
         if (mIam == null) return false;
@@ -370,6 +375,9 @@
         ActivityManager.StackInfo stackInfo = null;
         try {
             stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
+            if (stackInfo != null && stackInfo.userId != getCurrentUser()) {
+                return false;
+            }
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -465,7 +473,7 @@
 
         try {
             mIam.positionTaskInStack(taskId, stackId, 0);
-        } catch (RemoteException e) {
+        } catch (RemoteException | IllegalArgumentException e) {
             e.printStackTrace();
         }
     }
@@ -498,6 +506,18 @@
     }
 
     /**
+     * Sends a message to close other system windows.
+     */
+    public void sendCloseSystemWindows(String reason) {
+        if (ActivityManagerNative.isSystemReady()) {
+            try {
+                mIam.closeSystemDialogs(reason);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
      * Returns the activity info for a given component name.
      *
      * @param cn The component name of the activity.
@@ -634,7 +654,7 @@
         if (mPm == null) return null;
         if (RecentsDebugFlags.Static.EnableSystemServicesProxy) return null;
 
-        ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
+        ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
         ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
         if (defaultHomeActivity != null) {
             return defaultHomeActivity.getPackageName();
@@ -722,7 +742,7 @@
     /**
      * Returns the first Recents widget from the same package as the global assist activity.
      */
-    private AppWidgetProviderInfo resolveSearchAppWidget() {
+    public AppWidgetProviderInfo resolveSearchAppWidget() {
         if (mAssistComponent == null) return null;
         List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
@@ -764,6 +784,19 @@
     }
 
     /**
+     * Returns whether the current task is in screen-pinning mode.
+     */
+    public boolean isScreenPinningActive() {
+        if (mIam == null) return false;
+
+        try {
+            return mIam.isInLockTaskMode();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Returns a global setting.
      */
     public int getGlobalSetting(Context context, String setting) {
@@ -836,8 +869,7 @@
             ActivityOptions options) {
         if (mIam != null) {
             try {
-                mIam.startActivityFromRecents(
-                        taskId, INVALID_STACK_ID, options == null ? null : options.toBundle());
+                mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
                 return true;
             } catch (Exception e) {
                 Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
@@ -879,14 +911,29 @@
         }
     }
 
-    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+    public void registerDockedStackListener(IDockedStackListener listener) {
         if (mWm == null) return;
 
         try {
-            WindowManagerGlobal.getWindowManagerService().registerDockDividerVisibilityListener(
-                    listener);
+            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
+
+    /**
+     * Calculates the size of the dock divider in the current orientation.
+     */
+    public int getDockedDividerSize(Context context) {
+        Resources res = context.getResources();
+        int dividerWindowWidth = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
+        int dividerInsets = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_insets);
+        return dividerWindowWidth - 2 * dividerInsets;
+    }
+
+    public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) {
+        mWm.requestAppKeyboardShortcuts(receiver);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 2bf2ccb..33f116b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -18,13 +18,43 @@
 
 import android.animation.Animator;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.IntProperty;
+import android.util.Property;
 import android.view.View;
 import android.view.ViewParent;
 
 /* Common code */
 public class Utilities {
 
+    public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
+            new IntProperty<Drawable>("drawableAlpha") {
+                @Override
+                public void setValue(Drawable object, int alpha) {
+                    object.setAlpha(alpha);
+                }
+
+                @Override
+                public Integer get(Drawable object) {
+                    return object.getAlpha();
+                }
+            };
+
+    public static final Property<Drawable, Rect> DRAWABLE_RECT =
+            new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
+                @Override
+                public void set(Drawable object, Rect bounds) {
+                    object.setBounds(bounds);
+                }
+
+                @Override
+                public Rect get(Drawable object) {
+                    return object.getBounds();
+                }
+            };
+
     /**
      * @return the first parent walking up the view hierarchy that has the given class type.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 7a92b2a..822ad77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -25,8 +25,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArraySet;
-import android.util.Log;
+
 import com.android.systemui.Prefs;
+import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -115,14 +116,14 @@
      * - least-recent to most-recent freeform tasks
      */
     public synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        SystemServicesProxy ssp = Recents.getSystemServices();
         Resources res = mContext.getResources();
         ArrayList<Task> allTasks = new ArrayList<>();
         if (mRawTasks == null) {
             preloadRawTasks(isTopTaskHome);
         }
 
+        String dismissDescFormat = mContext.getString(
+                R.string.accessibility_recents_item_will_be_dismissed);
         long lastStackActiveTime = Prefs.getLong(mContext,
                 Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
         long newLastStackActiveTime = -1;
@@ -146,6 +147,7 @@
             // Load the title, icon, and color
             String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
             String contentDescription = loader.getAndUpdateContentDescription(taskKey, title, res);
+            String dismissDescription = String.format(dismissDescFormat, contentDescription);
             Drawable icon = isStackTask
                     ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                     : null;
@@ -154,8 +156,8 @@
 
             // Add the task to the stack
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
-                    thumbnail, title, contentDescription, activityColor, !isStackTask,
-                    (i == (taskCount - 1)), config.lockToAppEnabled, t.bounds, t.taskDescription);
+                    thumbnail, title, contentDescription, dismissDescription, activityColor,
+                    !isStackTask, t.bounds, t.taskDescription);
 
             allTasks.add(task);
         }
@@ -222,7 +224,7 @@
     /** Returns whether there are any tasks in any stacks. */
     public boolean hasTasks() {
         if (mStack != null) {
-            return mStack.getStackTaskCount() > 0;
+            return mStack.getTaskCount() > 0;
         }
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 34a0e52..29e7077 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -54,6 +54,8 @@
         public long firstActiveTime;
         public long lastActiveTime;
 
+        private int mHashCode;
+
         public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
                 long lastActiveTime) {
             this.id = id;
@@ -62,6 +64,12 @@
             this.userId = userId;
             this.firstActiveTime = firstActiveTime;
             this.lastActiveTime = lastActiveTime;
+            updateHashCode();
+        }
+
+        public void setStackId(int stackId) {
+            this.stackId = stackId;
+            updateHashCode();
         }
 
         public ComponentName getComponent() {
@@ -79,7 +87,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(id, stackId, userId);
+            return mHashCode;
         }
 
         @Override
@@ -90,6 +98,10 @@
                     + "lat: " + lastActiveTime + ", "
                     + getComponent().getPackageName();
         }
+
+        private void updateHashCode() {
+            mHashCode = Objects.hash(id, stackId, userId);
+        }
     }
 
     public TaskKey key;
@@ -113,6 +125,7 @@
     public Bitmap thumbnail;
     public String title;
     public String contentDescription;
+    public String dismissDescription;
     public int colorPrimary;
     public boolean useLightOnPrimaryColor;
 
@@ -131,8 +144,6 @@
      */
     public boolean isLaunchTarget;
     public boolean isHistorical;
-    public boolean lockToThisTask;
-    public boolean lockToTaskEnabled;
 
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
@@ -141,8 +152,8 @@
     }
 
     public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
-                Bitmap thumbnail, String title, String contentDescription, int colorPrimary,
-                boolean isHistorical, boolean lockToThisTask, boolean lockToTaskEnabled,
+                Bitmap thumbnail, String title, String contentDescription,
+                String dismissDescription, int colorPrimary, boolean isHistorical,
                 Rect bounds, ActivityManager.TaskDescription taskDescription) {
         boolean isInAffiliationGroup = (affiliationTaskId != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
@@ -153,14 +164,13 @@
         this.thumbnail = thumbnail;
         this.title = title;
         this.contentDescription = contentDescription;
+        this.dismissDescription = dismissDescription;
         this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
         this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
                 Color.WHITE) > 3f;
         this.bounds = bounds;
         this.taskDescription = taskDescription;
         this.isHistorical = isHistorical;
-        this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
-        this.lockToTaskEnabled = lockToTaskEnabled;
     }
 
     /** Copies the other task. */
@@ -173,13 +183,12 @@
         this.thumbnail = o.thumbnail;
         this.title = o.title;
         this.contentDescription = o.contentDescription;
+        this.dismissDescription = o.dismissDescription;
         this.colorPrimary = o.colorPrimary;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
         this.bounds = o.bounds;
         this.isLaunchTarget = o.isLaunchTarget;
         this.isHistorical = o.isHistorical;
-        this.lockToThisTask = o.lockToThisTask;
-        this.lockToTaskEnabled = o.lockToTaskEnabled;
     }
 
     /**
@@ -207,7 +216,7 @@
      * Updates the stack id of this task.
      */
     public void setStackId(int stackId) {
-        key.stackId = stackId;
+        key.setStackId(stackId);
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
             mCallbacks.get(i).onTaskStackIdChanged();
@@ -236,8 +245,7 @@
     public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultApplicationIcon) {
         icon = defaultApplicationIcon;
         thumbnail = defaultThumbnail;
-        int callbackCount = mCallbacks.size();
-        for (int i = 0; i < callbackCount; i++) {
+        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
             mCallbacks.get(i).onTaskDataUnloaded();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
index 288f07c..15f6b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -1,7 +1,8 @@
 package com.android.systemui.recents.model;
 
+import android.util.ArrayMap;
+
 import java.util.ArrayList;
-import java.util.HashMap;
 
 /** Represents a grouping of tasks witihin a stack. */
 public class TaskGrouping {
@@ -11,7 +12,7 @@
 
     Task.TaskKey mFrontMostTaskKey;
     ArrayList<Task.TaskKey> mTaskKeys = new ArrayList<Task.TaskKey>();
-    HashMap<Task.TaskKey, Integer> mTaskKeyIndices = new HashMap<Task.TaskKey, Integer>();
+    ArrayMap<Task.TaskKey, Integer> mTaskKeyIndices = new ArrayMap<>();
 
     /** Creates a group with a specified affiliation. */
     public TaskGrouping(int affiliation) {
@@ -94,9 +95,10 @@
             return;
         }
 
+        int taskCount = mTaskKeys.size();
         mFrontMostTaskKey = mTaskKeys.get(mTaskKeys.size() - 1);
         mTaskKeyIndices.clear();
-        int taskCount = mTaskKeys.size();
+        mTaskKeyIndices.ensureCapacity(taskCount);
         for (int i = 0; i < taskCount; i++) {
             Task.TaskKey k = mTaskKeys.get(i);
             mTaskKeyIndices.put(k, i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 6f003ab..21d0bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,16 +16,27 @@
 
 package com.android.systemui.recents.model;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.SparseArray;
+import android.view.animation.Interpolator;
+import com.android.internal.policy.DockedDividerUtils;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.misc.NamedCounter;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -35,8 +46,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
 
@@ -44,6 +53,11 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
 
 
 /**
@@ -59,17 +73,14 @@
  */
 class FilteredTaskList {
 
-    private static final String TAG = "FilteredTaskList";
-    private static final boolean DEBUG = false;
-
     ArrayList<Task> mTasks = new ArrayList<>();
     ArrayList<Task> mFilteredTasks = new ArrayList<>();
-    HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<>();
+    ArrayMap<Task.TaskKey, Integer> mTaskIndices = new ArrayMap<>();
     TaskFilter mFilter;
 
     /** Sets the task filter, saving the current touch state */
     boolean setFilter(TaskFilter filter) {
-        ArrayList<Task> prevFilteredTasks = new ArrayList<Task>(mFilteredTasks);
+        ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
         mFilter = filter;
         updateFilteredTasks();
         if (!prevFilteredTasks.equals(mFilteredTasks)) {
@@ -180,8 +191,9 @@
 
     /** Updates the mapping of tasks to indices. */
     private void updateFilteredTaskIndices() {
-        mTaskIndices.clear();
         int taskCount = mFilteredTasks.size();
+        mTaskIndices.clear();
+        mTaskIndices.ensureCapacity(taskCount);
         for (int i = 0; i < taskCount; i++) {
             Task t = mFilteredTasks.get(i);
             mTaskIndices.put(t.key, i);
@@ -229,30 +241,36 @@
     public static class DockState implements DropTarget {
 
         private static final int DOCK_AREA_ALPHA = 192;
-        public static final DockState NONE = new DockState(-1, 96, null, null);
-        public static final DockState LEFT = new DockState(
+        public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, null, null, null);
+        public static final DockState LEFT = new DockState(DOCKED_LEFT,
                 DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
-                new RectF(0, 0, 0.15f, 1), new RectF(0, 0, 0.15f, 1));
-        public static final DockState TOP = new DockState(
+                new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
+                new RectF(0, 0, 0.5f, 1));
+        public static final DockState TOP = new DockState(DOCKED_TOP,
                 DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
-                new RectF(0, 0, 1, 0.15f), new RectF(0, 0, 1, 0.15f));
-        public static final DockState RIGHT = new DockState(
+                new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
+                new RectF(0, 0, 1, 0.5f));
+        public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
                 DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
-                new RectF(0.85f, 0, 1, 1), new RectF(0.85f, 0, 1, 1));
-        public static final DockState BOTTOM = new DockState(
+                new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
+                new RectF(0.5f, 0, 1, 1));
+        public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
                 DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
-                new RectF(0, 0.85f, 1, 1), new RectF(0, 0.85f, 1, 1));
+                new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
+                new RectF(0, 0.5f, 1, 1));
 
         @Override
-        public boolean acceptsDrop(int x, int y, int width, int height) {
-            return touchAreaContainsPoint(width, height, x, y);
+        public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+            return isCurrentTarget
+                    ? areaContainsPoint(expandedTouchDockArea, width, height, x, y)
+                    : areaContainsPoint(touchArea, width, height, x, y);
         }
 
         // Represents the view state of this dock state
         public class ViewState {
             public final int dockAreaAlpha;
             public final ColorDrawable dockAreaOverlay;
-            private ObjectAnimator dockAreaOverlayAnimator;
+            private AnimatorSet dockAreaOverlayAnimator;
 
             private ViewState(int alpha) {
                 dockAreaAlpha = alpha;
@@ -261,56 +279,130 @@
             }
 
             /**
-             * Creates a new alpha animation.
+             * Creates a new bounds and alpha animation.
              */
-            public void startAlphaAnimation(int alpha, int duration) {
+            public void startAnimation(Rect bounds, int alpha, int duration,
+                    Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
+                if (dockAreaOverlayAnimator != null) {
+                    dockAreaOverlayAnimator.cancel();
+                }
+
+                ArrayList<Animator> animators = new ArrayList<>();
                 if (dockAreaOverlay.getAlpha() != alpha) {
-                    if (dockAreaOverlayAnimator != null) {
-                        dockAreaOverlayAnimator.cancel();
+                    if (animateAlpha) {
+                        animators.add(ObjectAnimator.ofInt(dockAreaOverlay,
+                                Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), alpha));
+                    } else {
+                        dockAreaOverlay.setAlpha(alpha);
                     }
-                    dockAreaOverlayAnimator = ObjectAnimator.ofInt(dockAreaOverlay, "alpha", alpha);
+                }
+                if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
+                    if (animateBounds) {
+                        PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
+                                Utilities.DRAWABLE_RECT, new RectEvaluator(new Rect()),
+                                dockAreaOverlay.getBounds(), bounds);
+                        animators.add(ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop));
+                    } else {
+                        dockAreaOverlay.setBounds(bounds);
+                    }
+                }
+                if (!animators.isEmpty()) {
+                    dockAreaOverlayAnimator = new AnimatorSet();
+                    dockAreaOverlayAnimator.playTogether(animators);
                     dockAreaOverlayAnimator.setDuration(duration);
+                    dockAreaOverlayAnimator.setInterpolator(interpolator);
                     dockAreaOverlayAnimator.start();
                 }
             }
         }
 
+        public final int dockSide;
         public final int createMode;
         public final ViewState viewState;
-        private final RectF dockArea;
         private final RectF touchArea;
+        private final RectF dockArea;
+        private final RectF expandedTouchDockArea;
 
         /**
          * @param createMode used to pass to ActivityManager to dock the task
          * @param touchArea the area in which touch will initiate this dock state
          * @param dockArea the visible dock area
+         * @param expandedTouchDockArea the areain which touch will continue to dock after entering
+         *                              the initial touch area.  This is also the new dock area to
+         *                              draw.
          */
-        DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea) {
+        DockState(int dockSide, int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
+                  RectF expandedTouchDockArea) {
+            this.dockSide = dockSide;
             this.createMode = createMode;
             this.viewState = new ViewState(dockAreaAlpha);
             this.dockArea = dockArea;
             this.touchArea = touchArea;
+            this.expandedTouchDockArea = expandedTouchDockArea;
         }
 
         /**
-         * Returns whether {@param x} and {@param y} are contained in the touch area scaled to the
+         * Returns whether {@param x} and {@param y} are contained in the area scaled to the
          * given {@param width} and {@param height}.
          */
-        public boolean touchAreaContainsPoint(int width, int height, float x, float y) {
-            int left = (int) (touchArea.left * width);
-            int top = (int) (touchArea.top * height);
-            int right = (int) (touchArea.right * width);
-            int bottom = (int) (touchArea.bottom * height);
+        public boolean areaContainsPoint(RectF area, int width, int height, float x, float y) {
+            int left = (int) (area.left * width);
+            int top = (int) (area.top * height);
+            int right = (int) (area.right * width);
+            int bottom = (int) (area.bottom * height);
             return x >= left && y >= top && x <= right && y <= bottom;
         }
 
         /**
          * Returns the docked task bounds with the given {@param width} and {@param height}.
          */
-        public Rect getDockedBounds(int width, int height) {
+        public Rect getPreDockedBounds(int width, int height) {
             return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
                     (int) (dockArea.right * width), (int) (dockArea.bottom * height));
         }
+
+        /**
+         * Returns the expanded docked task bounds with the given {@param width} and
+         * {@param height}.
+         */
+        public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
+                Resources res) {
+            // Calculate the docked task bounds
+            boolean isHorizontalDivision =
+                    res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+            int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+                    insets, width, height, dividerSize);
+            Rect newWindowBounds = new Rect();
+            DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
+                    width, height, dividerSize);
+            return newWindowBounds;
+        }
+
+        /**
+         * Returns the task stack bounds with the given {@param width} and
+         * {@param height}.
+         */
+        public Rect getDockedTaskStackBounds(int width, int height, int dividerSize, Rect insets,
+                Resources res) {
+            RecentsConfiguration config = Recents.getConfiguration();
+
+            // Calculate the inverse docked task bounds
+            boolean isHorizontalDivision =
+                    res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+            int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+                    insets, width, height, dividerSize);
+            Rect newWindowBounds = new Rect();
+            DockedDividerUtils.calculateBoundsForPosition(position,
+                    DockedDividerUtils.invertDockSide(dockSide), newWindowBounds, width, height,
+                    dividerSize);
+
+            // Calculate the task stack bounds from the new window bounds
+            Rect searchBarSpaceBounds = new Rect();
+            Rect taskStackBounds = new Rect();
+            config.getTaskStackBounds(newWindowBounds, insets.top, insets.right,
+                    searchBarSpaceBounds, taskStackBounds);
+            return taskStackBounds;
+        }
     }
 
     // A comparator that sorts tasks by their last active time
@@ -344,7 +436,7 @@
     TaskStackCallbacks mCb;
 
     ArrayList<TaskGrouping> mGroups = new ArrayList<>();
-    HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<>();
+    ArrayMap<Integer, TaskGrouping> mAffinitiesGroups = new ArrayMap<>();
 
     public TaskStack() {
         // Ensure that we only show non-docked tasks
@@ -427,8 +519,6 @@
                 removeGroup(group);
             }
         }
-        // Update the lock-to-app state
-        t.lockToThisTask = false;
     }
 
     /** Removes a task */
@@ -437,9 +527,6 @@
             boolean wasFrontMostTask = (getStackFrontMostTask() == t);
             removeTaskImpl(mStackTaskList, t);
             Task newFrontMostTask = getStackFrontMostTask();
-            if (newFrontMostTask != null && newFrontMostTask.lockToTaskEnabled) {
-                newFrontMostTask.lockToThisTask = true;
-            }
             if (mCb != null) {
                 // Notify that a task has been removed
                 mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask);
@@ -451,6 +538,7 @@
                 mCb.onHistoryTaskRemoved(this, t);
             }
         }
+        mRawTaskList.remove(t);
     }
 
     /**
@@ -461,8 +549,8 @@
      */
     public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
         // Compute a has set for each of the tasks
-        HashMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
-        HashMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
+        ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
+        ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
 
         ArrayList<Task> newTasks = new ArrayList<>();
 
@@ -500,9 +588,6 @@
         // Sort all the tasks to ensure they are ordered correctly
         Collections.sort(newTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
 
-        // TODO: Update screen pinning for the new front-most task post refactoring lockToTask out
-        // of the Task
-
         // Filter out the historical tasks from this new list
         ArrayList<Task> stackTasks = new ArrayList<>();
         ArrayList<Task> historyTasks = new ArrayList<>();
@@ -524,10 +609,21 @@
         mAffinitiesGroups.clear();
     }
 
-    /** Gets the front task */
+    /**
+     * Gets the front-most task in the stack.
+     */
     public Task getStackFrontMostTask() {
-        if (mStackTaskList.size() == 0) return null;
-        return mStackTaskList.getTasks().get(mStackTaskList.size() - 1);
+        ArrayList<Task> stackTasks = mStackTaskList.getTasks();
+        if (stackTasks.isEmpty()) {
+            return null;
+        }
+        for (int i = stackTasks.size() - 1; i >= 0; i--) {
+            Task task = stackTasks.get(i);
+            if (!task.isFreeformTask()) {
+                return task;
+            }
+        }
+        return null;
     }
 
     /** Gets the task keys */
@@ -558,6 +654,22 @@
     }
 
     /**
+     * Returns the set of "freeform" tasks in the stack.
+     */
+    public ArrayList<Task> getFreeformTasks() {
+        ArrayList<Task> freeformTasks = new ArrayList<>();
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (task.isFreeformTask()) {
+                freeformTasks.add(task);
+            }
+        }
+        return freeformTasks;
+    }
+
+    /**
      * Computes a set of all the active and historical tasks ordered by their last active time.
      */
     public ArrayList<Task> computeAllTasksList() {
@@ -569,16 +681,32 @@
     }
 
     /**
-     * Returns the number of tasks in the active stack.
+     * Returns the number of stack and freeform tasks.
      */
-    public int getStackTaskCount() {
+    public int getTaskCount() {
         return mStackTaskList.size();
     }
 
     /**
-     * Returns the number of freeform tasks in the active stack.
+     * Returns the number of stack tasks.
      */
-    public int getStackTaskFreeformCount() {
+    public int getStackTaskCount() {
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int stackCount = 0;
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
+            if (!task.isFreeformTask()) {
+                stackCount++;
+            }
+        }
+        return stackCount;
+    }
+
+    /**
+     * Returns the number of freeform tasks.
+     */
+    public int getFreeformTaskCount() {
         ArrayList<Task> tasks = mStackTaskList.getTasks();
         int freeformCount = 0;
         int taskCount = tasks.size();
@@ -645,7 +773,7 @@
      */
     public void createAffiliatedGroupings(Context context) {
         if (RecentsDebugFlags.Static.EnableSimulatedTaskGroups) {
-            HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
+            ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
             // Sort all tasks by increasing firstActiveTime of the task
             ArrayList<Task> tasks = mStackTaskList.getTasks();
             Collections.sort(tasks, new Comparator<Task>() {
@@ -710,9 +838,10 @@
             mStackTaskList.set(tasks);
         } else {
             // Create the task groups
-            HashMap<Task.TaskKey, Task> tasksMap = new HashMap<>();
+            ArrayMap<Task.TaskKey, Task> tasksMap = new ArrayMap<>();
             ArrayList<Task> tasks = mStackTaskList.getTasks();
             int taskCount = tasks.size();
+            tasksMap.ensureCapacity(taskCount);
             for (int i = 0; i < taskCount; i++) {
                 Task t = tasks.get(i);
                 TaskGrouping group;
@@ -754,12 +883,12 @@
      * Computes the components of tasks in this stack that have been removed as a result of a change
      * in the specified package.
      */
-    public HashSet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
+    public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
         // Identify all the tasks that should be removed as a result of the package being removed.
         // Using a set to ensure that we callback once per unique component.
         SystemServicesProxy ssp = Recents.getSystemServices();
-        HashSet<ComponentName> existingComponents = new HashSet<>();
-        HashSet<ComponentName> removedComponents = new HashSet<>();
+        ArraySet<ComponentName> existingComponents = new ArraySet<>();
+        ArraySet<ComponentName> removedComponents = new ArraySet<>();
         ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
         for (Task.TaskKey t : taskKeys) {
             // Skip if this doesn't apply to the current user
@@ -797,8 +926,8 @@
     /**
      * Given a list of tasks, returns a map of each task's key to the task.
      */
-    private HashMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
-        HashMap<Task.TaskKey, Task> map = new HashMap<>();
+    private ArrayMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
+        ArrayMap<Task.TaskKey, Task> map = new ArrayMap<>(tasks.size());
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
             Task task = tasks.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index c0b8a9d..b8bbf51 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -18,8 +18,6 @@
 
 import android.graphics.Outline;
 import android.graphics.Rect;
-import android.util.IntProperty;
-import android.util.Property;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
@@ -29,23 +27,11 @@
     View mSourceView;
     Rect mClipRect = new Rect();
     Rect mClipBounds = new Rect();
+    Rect mLastClipBounds = new Rect();
     int mCornerRadius;
     float mAlpha = 1f;
     final float mMinAlpha = 0.25f;
 
-    public static final Property<AnimateableViewBounds, Integer> CLIP_BOTTOM =
-            new IntProperty<AnimateableViewBounds>("clipBottom") {
-                @Override
-                public void setValue(AnimateableViewBounds object, int clip) {
-                    object.setClipBottom(clip, false /* force */);
-                }
-
-                @Override
-                public Integer get(AnimateableViewBounds object) {
-                    return object.getClipBottom();
-                }
-            };
-
     public AnimateableViewBounds(View source, int cornerRadius) {
         mSourceView = source;
         mCornerRadius = cornerRadius;
@@ -77,11 +63,9 @@
     }
 
     /** Sets the bottom clip. */
-    public void setClipBottom(int bottom, boolean force) {
-        if (bottom != mClipRect.bottom || force) {
-            mClipRect.bottom = bottom;
-            updateClipBounds();
-        }
+    public void setClipBottom(int bottom) {
+        mClipRect.bottom = bottom;
+        updateClipBounds();
     }
 
     /** Returns the bottom clip. */
@@ -93,7 +77,10 @@
         mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
                 mSourceView.getWidth() - Math.max(0, mClipRect.right),
                 mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
-        mSourceView.setClipBounds(mClipBounds);
-        mSourceView.invalidateOutline();
+        if (!mLastClipBounds.equals(mClipBounds)) {
+            mSourceView.setClipBounds(mClipBounds);
+            mSourceView.invalidateOutline();
+            mLastClipBounds.set(mClipBounds);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
index 8ae00a7..3ad368c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
@@ -25,5 +25,5 @@
      * Returns whether this target can accept this drop.  The x,y are relative to the top level
      * RecentsView, and the width/height are of the RecentsView.
      */
-    boolean acceptsDrop(int x, int y, int width, int height);
+    boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 7f907ef..d3a1e91 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -17,15 +17,12 @@
 package com.android.systemui.recents.views;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.graphics.RectF;
-import android.util.Log;
-
+import android.util.ArrayMap;
 import com.android.systemui.R;
 import com.android.systemui.recents.model.Task;
 
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -33,11 +30,8 @@
  */
 public class FreeformWorkspaceLayoutAlgorithm {
 
-    private static final String TAG = "FreeformWorkspaceLayoutAlgorithm";
-    private static final boolean DEBUG = false;
-
     // Optimization, allows for quick lookup of task -> rect
-    private HashMap<Task.TaskKey, RectF> mTaskRectMap = new HashMap<>();
+    private ArrayMap<Task.TaskKey, RectF> mTaskRectMap = new ArrayMap<>();
 
     private int mTaskPadding;
 
@@ -54,6 +48,7 @@
     public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
         Collections.reverse(freeformTasks);
         mTaskRectMap.clear();
+        mTaskRectMap.ensureCapacity(freeformTasks.size());
 
         int numFreeformTasks = stackLayout.mNumFreeformTasks;
         if (!freeformTasks.isEmpty()) {
@@ -152,35 +147,15 @@
     public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
             TaskStackLayoutAlgorithm stackLayout) {
         if (mTaskRectMap.containsKey(task.key)) {
-            final Rect taskRect = stackLayout.mTaskRect;
             final RectF ffRect = mTaskRectMap.get(task.key);
 
             transformOut.scale = 1f;
             transformOut.alpha = 1f;
             transformOut.translationZ = stackLayout.mMaxTranslationZ;
-            if (task.thumbnail != null) {
-                if (task.bounds == null) {
-                    // This is a stack task that has no freeform thumbnail, so keep the same bitmap
-                    // scale as it had in the stack
-                    transformOut.thumbnailScale = (float) taskRect.width() /
-                            task.thumbnail.getWidth();
-                } else {
-                    // This is a freeform rect so fit the bitmap to the task bounds
-                    transformOut.thumbnailScale = Math.min(
-                            ffRect.width() / task.thumbnail.getWidth(),
-                            ffRect.height() / task.thumbnail.getHeight());
-                }
-            } else {
-                transformOut.thumbnailScale = 1f;
-            }
             transformOut.rect.set(ffRect);
             transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top);
             transformOut.visible = true;
             transformOut.p = 1f;
-
-            if (DEBUG) {
-                Log.d(TAG, "getTransform: " + task.key + ", " + transformOut);
-            }
             return transformOut;
         }
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 0af7c1e..b363ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -32,15 +32,15 @@
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.WindowManagerGlobal;
 import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.recents.ExitRecentsWindowFirstAnimationFrameEvent;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -90,7 +90,7 @@
      */
     public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
             final TaskStackView stackView, final TaskView taskView,
-            final boolean lockToTask, final Rect bounds, int destinationStack) {
+            final boolean screenPinningRequested, final Rect bounds, int destinationStack) {
         final ActivityOptions opts = ActivityOptions.makeBasic();
         if (bounds != null) {
             opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
@@ -109,7 +109,7 @@
                     EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
                     EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
 
-                    if (lockToTask) {
+                    if (screenPinningRequested) {
                         // Request screen pinning after the animation runs
                         mHandler.postDelayed(mStartScreenPinningRunnable, 350);
                     }
@@ -131,16 +131,19 @@
             // task views, and we can launch immediately
             startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener);
         } else {
+            LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
+                    screenPinningRequested);
             if (task.group != null && !task.group.isFrontMostTask(task)) {
-                stackView.startLaunchTaskAnimation(taskView, new Runnable() {
+                launchStartedEvent.addPostAnimationCallback(new Runnable() {
                     @Override
                     public void run() {
                         startTaskActivity(stack, task, taskView, opts, transitionFuture,
                                 animStartedListener);
                     }
-                }, lockToTask);
+                });
+                EventBus.getDefault().send(launchStartedEvent);
             } else {
-                stackView.startLaunchTaskAnimation(taskView, null, lockToTask);
+                EventBus.getDefault().send(launchStartedEvent);
                 startTaskActivity(stack, task, taskView, opts, transitionFuture,
                         animStartedListener);
             }
@@ -162,12 +165,12 @@
             int taskIndexFromFront = 0;
             int taskIndex = stack.indexOfStackTask(task);
             if (taskIndex > -1) {
-                taskIndexFromFront = stack.getStackTaskCount() - taskIndex - 1;
+                taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
             }
             EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
         } else {
             // Dismiss the task if we fail to launch it
-            EventBus.getDefault().send(new DismissTaskViewEvent(task, taskView));
+            taskView.dismissTask();
 
             // Keep track of failed launches
             EventBus.getDefault().send(new LaunchTaskFailedEvent());
@@ -286,7 +289,7 @@
                     //       never happen)
                     specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
                 } else {
-                    layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+                    layoutAlgorithm.getStackTransform(t, stackScroll, mTmpTransform, null);
                     specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 9b1315a..0d9a1c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,8 +16,9 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -32,8 +33,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
-import com.android.internal.logging.MetricsLogger;
 import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsActivity;
@@ -43,7 +44,6 @@
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
@@ -63,8 +63,10 @@
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -75,8 +77,7 @@
  */
 public class RecentsView extends FrameLayout {
 
-    private static final String TAG = "RecentsView";
-    private static final boolean DEBUG = false;
+    private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135;
 
     private final Handler mHandler;
 
@@ -88,15 +89,10 @@
     private boolean mAwaitingFirstLayout = true;
     private boolean mLastTaskLaunchedWasFreeform;
     private Rect mSystemInsets = new Rect();
+    private int mDividerSize;
 
     private RecentsTransitionHelper mTransitionHelper;
     private RecentsViewTouchHandler mTouchHandler;
-    private TaskStack.DockState[] mVisibleDockStates = {
-            TaskStack.DockState.LEFT,
-            TaskStack.DockState.TOP,
-            TaskStack.DockState.RIGHT,
-            TaskStack.DockState.BOTTOM,
-    };
 
     private final Interpolator mFastOutSlowInInterpolator;
     private final Interpolator mFastOutLinearInInterpolator;
@@ -116,14 +112,16 @@
 
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        Resources res = context.getResources();
         setWillNotDraw(false);
+
+        SystemServicesProxy ssp = Recents.getSystemServices();
         mHandler = new Handler();
         mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_linear_in);
+        mDividerSize = ssp.getDockedDividerSize(context);
         mTouchHandler = new RecentsViewTouchHandler(this);
         mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
 
@@ -145,14 +143,11 @@
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
         mStack = stack;
-        // Disable reusing task stack views until the visibility bug is fixed. b/25998134
-        if (false && launchState.launchedReuseTaskStackViews) {
+        if (launchState.launchedReuseTaskStackViews) {
             if (mTaskStackView != null) {
                 // If onRecentsHidden is not triggered, we need to the stack view again here
                 mTaskStackView.reset();
                 mTaskStackView.setStack(stack);
-                removeView(mTaskStackView);
-                addView(mTaskStackView);
             } else {
                 mTaskStackView = new TaskStackView(getContext(), stack);
                 addView(mTaskStackView);
@@ -166,7 +161,7 @@
         }
 
         // Update the top level view's visibilities
-        if (stack.getStackTaskCount() > 0) {
+        if (stack.getTaskCount() > 0) {
             hideEmptyView();
         } else {
             showEmptyView();
@@ -216,7 +211,6 @@
     /** Launches the focused task from the first stack if possible */
     public boolean launchFocusedTask() {
         if (mTaskStackView != null) {
-            TaskStack stack = mTaskStackView.getStack();
             Task task = mTaskStackView.getFocusedTask();
             if (task != null) {
                 TaskView taskView = mTaskStackView.getChildViewForTask(task);
@@ -246,7 +240,6 @@
     /** Launches a given task. */
     public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
         if (mTaskStackView != null) {
-            TaskStack stack = mTaskStackView.getStack();
             // Iterate the stack views and try and find the given task.
             List<TaskView> taskViews = mTaskStackView.getTaskViews();
             int taskViewCount = taskViews.size();
@@ -262,39 +255,6 @@
         return false;
     }
 
-    /** Requests all task stacks to start their enter-recents animation */
-    public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
-        // We have to increment/decrement the post animation trigger in case there are no children
-        // to ensure that it runs
-        ctx.postAnimationTrigger.increment();
-        if (mTaskStackView != null) {
-            mTaskStackView.startEnterRecentsAnimation(ctx);
-        }
-        ctx.postAnimationTrigger.decrement();
-    }
-
-    /** Requests all task stacks to start their exit-recents animation */
-    public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
-        // We have to increment/decrement the post animation trigger in case there are no children
-        // to ensure that it runs
-        ctx.postAnimationTrigger.increment();
-        if (mTaskStackView != null) {
-            mTaskStackView.startExitToHomeAnimation(ctx);
-        }
-        ctx.postAnimationTrigger.decrement();
-
-        // Hide the history button
-        int taskViewExitToHomeDuration = getResources().getInteger(
-                R.integer.recents_task_exit_to_home_duration);
-        hideHistoryButton(taskViewExitToHomeDuration);
-
-        // If we are going home, cancel the previous task's window transition
-        EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
-
-        // Notify sof the exit animation
-        EventBus.getDefault().send(new DismissRecentsToHomeAnimationStarted());
-    }
-
     /** Adds the search bar */
     public void setSearchBar(RecentsAppWidgetHostView searchBar) {
         // Remove the previous search bar if one exists
@@ -317,7 +277,7 @@
      * Hides the task stack and shows the empty view.
      */
     public void showEmptyView() {
-        if (!RecentsDebugFlags.Static.DisableSearchBar && (mSearchBar != null)) {
+        if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) {
             mSearchBar.setVisibility(View.INVISIBLE);
         }
         mTaskStackView.setVisibility(View.INVISIBLE);
@@ -332,7 +292,7 @@
     public void hideEmptyView() {
         mEmptyView.setVisibility(View.INVISIBLE);
         mTaskStackView.setVisibility(View.VISIBLE);
-        if (!RecentsDebugFlags.Static.DisableSearchBar && (mSearchBar != null)) {
+        if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) {
             mSearchBar.setVisibility(View.VISIBLE);
         }
         mTaskStackView.bringToFront();
@@ -469,8 +429,9 @@
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
-        for (int i = mVisibleDockStates.length - 1; i >= 0; i--) {
-            Drawable d = mVisibleDockStates[i].viewState.dockAreaOverlay;
+        ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+        for (int i = visDockStates.size() - 1; i >= 0; i--) {
+            Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
             if (d.getAlpha() > 0) {
                 d.draw(canvas);
             }
@@ -479,8 +440,9 @@
 
     @Override
     protected boolean verifyDrawable(Drawable who) {
-        for (int i = mVisibleDockStates.length - 1; i >= 0; i--) {
-            Drawable d = mVisibleDockStates[i].viewState.dockAreaOverlay;
+        ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+        for (int i = visDockStates.size() - 1; i >= 0; i--) {
+            Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
             if (d == who) {
                 return true;
             }
@@ -496,52 +458,89 @@
                 event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack);
     }
 
+    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
+        // Hide the history button
+        int taskViewExitToHomeDuration = getResources().getInteger(
+                R.integer.recents_task_exit_to_home_duration);
+        hideHistoryButton(taskViewExitToHomeDuration);
+
+        // If we are going home, cancel the previous task's window transition
+        EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+    }
+
     public final void onBusEvent(DragStartEvent event) {
         updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
-                TaskStack.DockState.NONE.viewState.dockAreaAlpha);
+                true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+                true /* animateAlpha */, false /* animateBounds */);
     }
 
     public final void onBusEvent(DragDropTargetChangedEvent event) {
         if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
             updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
-                    TaskStack.DockState.NONE.viewState.dockAreaAlpha);
+                    true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+                    true /* animateAlpha */, true /* animateBounds */);
         } else {
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
-            updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, -1);
+            updateVisibleDockRegions(new TaskStack.DockState[] {dockState},
+                    false /* isDefaultDockState */, -1, true /* animateAlpha */,
+                    true /* animateBounds */);
         }
     }
 
     public final void onBusEvent(final DragEndEvent event) {
-        // Animate the overlay alpha back to 0
-        updateVisibleDockRegions(null, -1);
-
         // Handle the case where we drop onto a dock region
         if (event.dropTarget instanceof TaskStack.DockState) {
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
 
-            // Remove the task after it is docked
-            event.taskView.animate()
-                    .alpha(0f)
-                    .setDuration(150)
-                    .setInterpolator(mFastOutLinearInInterpolator)
-                    .setUpdateListener(null)
-                    .setListener(null)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mTaskStackView.getStack().removeTask(event.task);
-                        }
-                    })
-                    .withLayer()
-                    .start();
+            // Hide the dock region
+            updateVisibleDockRegions(null, false /* isDefaultDockState */, -1,
+                    false /* animateAlpha */, false /* animateBounds */);
 
-            // Dock the task and launch it
-            SystemServicesProxy ssp = Recents.getSystemServices();
-            ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
-            launchTask(event.task, null, INVALID_STACK_ID);
+            TaskStackLayoutAlgorithm stackLayout = mTaskStackView.getStackAlgorithm();
+            TaskStackViewScroller stackScroller = mTaskStackView.getScroller();
+            TaskViewTransform tmpTransform = new TaskViewTransform();
+
+            // We translated the view but we need to animate it back from the current layout-space
+            // rect to its final layout-space rect
+            int x = (int) event.taskView.getTranslationX();
+            int y = (int) event.taskView.getTranslationY();
+            Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(),
+                    event.taskView.getRight(), event.taskView.getBottom());
+            taskViewRect.offset(x, y);
+            event.taskView.setTranslationX(0);
+            event.taskView.setTranslationY(0);
+            event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
+                    taskViewRect.right, taskViewRect.bottom);
+
+            // Remove the task view after it is docked
+            mTaskStackView.updateLayout(false /* boundScroll */);
+            stackLayout.getStackTransform(event.task, stackScroller.getStackScroll(), tmpTransform,
+                    null);
+            tmpTransform.alpha = 0;
+            tmpTransform.scale = 1f;
+            tmpTransform.rect.set(taskViewRect);
+            mTaskStackView.updateTaskViewToTransform(event.taskView, tmpTransform,
+                    new TaskViewAnimation(125, PhoneStatusBar.ALPHA_OUT,
+                            new AnimatorListenerAdapter() {
+                                @Override
+                                public void onAnimationEnd(Animator animation) {
+                                    // Dock the task and launch it
+                                    SystemServicesProxy ssp = Recents.getSystemServices();
+                                    ssp.startTaskInDockedMode(getContext(), event.task.key.id,
+                                            dockState.createMode);
+                                    launchTask(event.task, null, INVALID_STACK_ID);
+
+                                    mTaskStackView.getStack().removeTask(event.task);
+                                }
+                            }));
+
 
             MetricsLogger.action(mContext,
                     MetricsLogger.ACTION_WINDOW_DOCK_DRAG_DROP);
+        } else {
+            // Animate the overlay alpha back to 0
+            updateVisibleDockRegions(null, true /* isDefaultDockState */, -1,
+                    true /* animateAlpha */, false /* animateBounds */);
         }
     }
 
@@ -613,21 +612,23 @@
 
     private void showHistoryButton(final int duration,
             final ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
-        mHistoryButton.setVisibility(View.VISIBLE);
-        mHistoryButton.setAlpha(0f);
         mHistoryButton.setText(getContext().getString(R.string.recents_history_label_format,
                 mStack.getHistoricalTasks().size()));
-        postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-            @Override
-            public void run() {
-                mHistoryButton.animate()
-                        .alpha(1f)
-                        .setDuration(duration)
-                        .setInterpolator(mFastOutSlowInInterpolator)
-                        .withLayer()
-                        .start();
-            }
-        });
+        if (mHistoryButton.getVisibility() == View.INVISIBLE) {
+            mHistoryButton.setVisibility(View.VISIBLE);
+            mHistoryButton.setAlpha(0f);
+            postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    mHistoryButton.animate()
+                            .alpha(1f)
+                            .setDuration(duration)
+                            .setInterpolator(mFastOutSlowInInterpolator)
+                            .withLayer()
+                            .start();
+                }
+            });
+        }
     }
 
     /**
@@ -641,44 +642,55 @@
 
     private void hideHistoryButton(int duration,
             final ReferenceCountedTrigger postHideStackAnimationTrigger) {
-        mHistoryButton.animate()
-                .alpha(0f)
-                .setDuration(duration)
-                .setInterpolator(mFastOutLinearInInterpolator)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        mHistoryButton.setVisibility(View.INVISIBLE);
-                        postHideStackAnimationTrigger.decrement();
-                    }
-                })
-                .withLayer()
-                .start();
-        postHideStackAnimationTrigger.increment();
+        if (mHistoryButton.getVisibility() == View.VISIBLE) {
+            mHistoryButton.animate()
+                    .alpha(0f)
+                    .setDuration(duration)
+                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mHistoryButton.setVisibility(View.INVISIBLE);
+                            postHideStackAnimationTrigger.decrement();
+                        }
+                    })
+                    .withLayer()
+                    .start();
+            postHideStackAnimationTrigger.increment();
+        }
     }
 
     /**
      * Updates the dock region to match the specified dock state.
      */
-    private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, int overrideAlpha) {
+    private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
+            boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha,
+            boolean animateBounds) {
         ArraySet<TaskStack.DockState> newDockStatesSet = new ArraySet<>();
         if (newDockStates != null) {
-            for (TaskStack.DockState dockState : newDockStates) {
-                newDockStatesSet.add(dockState);
-            }
+            Collections.addAll(newDockStatesSet, newDockStates);
         }
-        for (TaskStack.DockState dockState : mVisibleDockStates) {
+        ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates();
+        for (int i = visDockStates.size() - 1; i >= 0; i--) {
+            TaskStack.DockState dockState = visDockStates.get(i);
             TaskStack.DockState.ViewState viewState = dockState.viewState;
             if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
                 // This is no longer visible, so hide it
-                viewState.startAlphaAnimation(0, 150);
+                viewState.startAnimation(null, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
+                        PhoneStatusBar.ALPHA_OUT, animateAlpha, animateBounds);
             } else {
                 // This state is now visible, update the bounds and show it
                 int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha);
-                viewState.dockAreaOverlay.setBounds(
-                        dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
-                viewState.dockAreaOverlay.setCallback(this);
-                viewState.startAlphaAnimation(alpha, 150);
+                Rect bounds = isDefaultDockState
+                        ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight())
+                        : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
+                        mDividerSize, mSystemInsets, getResources());
+                if (viewState.dockAreaOverlay.getCallback() != this) {
+                    viewState.dockAreaOverlay.setCallback(this);
+                    viewState.dockAreaOverlay.setBounds(bounds);
+                }
+                viewState.startAnimation(bounds, alpha, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
+                        PhoneStatusBar.ALPHA_IN, animateAlpha, animateBounds);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 318801d..d8698ee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -19,6 +19,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.EventBus;
@@ -26,7 +27,6 @@
 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -58,9 +58,6 @@
  */
 public class RecentsViewTouchHandler {
 
-    private static final String TAG = "RecentsViewTouchHandler";
-    private static final boolean DEBUG = false;
-
     private RecentsView mRv;
 
     private Task mDragTask;
@@ -68,13 +65,17 @@
 
     private Point mTaskViewOffset = new Point();
     private Point mDownPos = new Point();
-    private boolean mDragging;
+    private boolean mDragRequested;
+    private boolean mIsDragging;
+    private float mDragSlop;
 
     private DropTarget mLastDropTarget;
     private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
+    private ArrayList<TaskStack.DockState> mVisibleDockStates = new ArrayList<>();
 
     public RecentsViewTouchHandler(RecentsView rv) {
         mRv = rv;
+        mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
     }
 
     /**
@@ -97,16 +98,23 @@
         return dockStates;
     }
 
+    /**
+     * Returns the set of visible dock states for this current drag.
+     */
+    public ArrayList<TaskStack.DockState> getVisibleDockStates() {
+        return mVisibleDockStates;
+    }
+
     /** Touch preprocessing for handling below */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         handleTouchEvent(ev);
-        return mDragging;
+        return mDragRequested;
     }
 
     /** Handles touch events once we have intercepted them */
     public boolean onTouchEvent(MotionEvent ev) {
         handleTouchEvent(ev);
-        return mDragging;
+        return mDragRequested;
     }
 
     /**** Events ****/
@@ -114,7 +122,9 @@
     public final void onBusEvent(DragStartEvent event) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         mRv.getParent().requestDisallowInterceptTouchEvent(true);
-        mDragging = true;
+        mDragRequested = true;
+        // We defer starting the actual drag handling until the user moves past the drag slop
+        mIsDragging = false;
         mDragTask = event.task;
         mTaskView = event.taskView;
         mDropTargets.clear();
@@ -128,12 +138,13 @@
         mTaskView.setTranslationX(x);
         mTaskView.setTranslationY(y);
 
-        RecentsConfiguration config = Recents.getConfiguration();
-        if (!ssp.hasDockedTask()) {
+        mVisibleDockStates.clear();
+        if (!ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) {
             // Add the dock state drop targets (these take priority)
             TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
             for (TaskStack.DockState dockState : dockStates) {
                 registerDropTargetForCurrentDrag(dockState);
+                mVisibleDockStates.add(dockState);
             }
         }
 
@@ -142,7 +153,7 @@
     }
 
     public final void onBusEvent(DragEndEvent event) {
-        mDragging = false;
+        mDragRequested = false;
         mDragTask = null;
         mTaskView = null;
         mLastDropTarget = null;
@@ -150,7 +161,6 @@
 
     /**
      * Handles dragging touch events
-     * @param ev
      */
     private void handleTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
@@ -159,25 +169,45 @@
                 mDownPos.set((int) ev.getX(), (int) ev.getY());
                 break;
             case MotionEvent.ACTION_MOVE: {
-                if (mDragging) {
-                    int width = mRv.getMeasuredWidth();
-                    int height = mRv.getMeasuredHeight();
-                    float evX = ev.getX();
-                    float evY = ev.getY();
-                    float x = evX - mTaskViewOffset.x;
-                    float y = evY - mTaskViewOffset.y;
+                float evX = ev.getX();
+                float evY = ev.getY();
+                float x = evX - mTaskViewOffset.x;
+                float y = evY - mTaskViewOffset.y;
 
-                    DropTarget currentDropTarget = null;
-                    for (DropTarget target : mDropTargets) {
-                        if (target.acceptsDrop((int) evX, (int) evY, width, height)) {
-                            currentDropTarget = target;
-                            break;
-                        }
+                if (mDragRequested) {
+                    if (!mIsDragging) {
+                        mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
                     }
-                    if (mLastDropTarget != currentDropTarget) {
-                        mLastDropTarget = currentDropTarget;
-                        EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
-                                currentDropTarget));
+                    if (mIsDragging) {
+                        int width = mRv.getMeasuredWidth();
+                        int height = mRv.getMeasuredHeight();
+
+                        DropTarget currentDropTarget = null;
+
+                        // Give priority to the current drop target to retain the touch handling
+                        if (mLastDropTarget != null) {
+                            if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
+                                    true /* isCurrentTarget */)) {
+                                currentDropTarget = mLastDropTarget;
+                            }
+                        }
+
+                        // Otherwise, find the next target to handle this event
+                        if (currentDropTarget == null) {
+                            for (DropTarget target : mDropTargets) {
+                                if (target.acceptsDrop((int) evX, (int) evY, width, height,
+                                        false /* isCurrentTarget */)) {
+                                    currentDropTarget = target;
+                                    break;
+                                }
+                            }
+                        }
+                        if (mLastDropTarget != currentDropTarget) {
+                            mLastDropTarget = currentDropTarget;
+                            EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
+                                    currentDropTarget));
+                        }
+
                     }
 
                     mTaskView.setTranslationX(x);
@@ -187,7 +217,7 @@
             }
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL: {
-                if (mDragging) {
+                if (mDragRequested) {
                     EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
                             mLastDropTarget));
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
deleted file mode 100644
index b7c1de3..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-/**
- * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
- * by the class hosting the views that need to swiped, and, using this interface, handles touch
- * events and translates / fades / animates the view as it is dismissed.
- */
-public class SwipeHelper {
-    static final String TAG = "SwipeHelper";
-    private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
-    private static final boolean CONSTRAIN_SWIPE = true;
-    private static final boolean FADE_OUT_DURING_SWIPE = true;
-    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
-    public static final int X = 0;
-    public static final int Y = 1;
-
-    private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
-    private Interpolator mLinearOutSlowInInterpolator;
-
-    private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
-    private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
-    private int MAX_ESCAPE_ANIMATION_DURATION = 150; // ms
-    private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
-
-    public static float ALPHA_FADE_START = 0.15f; // fraction of thumbnail width
-                                                 // where fade starts
-    static final float ALPHA_FADE_END = 0.65f; // fraction of thumbnail width
-                                              // beyond which alpha->0
-    private float mMinAlpha = 0f;
-
-    private float mPagingTouchSlop;
-    Callback mCallback;
-    private int mSwipeDirection;
-    private VelocityTracker mVelocityTracker;
-
-    private float mInitialTouchPos;
-    private boolean mDragging;
-    private float mSnapBackTranslationX;
-
-    private View mCurrView;
-    private boolean mCanCurrViewBeDimissed;
-    private float mDensityScale;
-
-    public boolean mAllowSwipeTowardsStart = true;
-    public boolean mAllowSwipeTowardsEnd = true;
-    private boolean mRtl;
-
-    public SwipeHelper(Context context, int swipeDirection, Callback callback, float densityScale,
-            float pagingTouchSlop) {
-        mCallback = callback;
-        mSwipeDirection = swipeDirection;
-        mVelocityTracker = VelocityTracker.obtain();
-        mDensityScale = densityScale;
-        mPagingTouchSlop = pagingTouchSlop;
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.linear_out_slow_in);
-    }
-
-    public void setDensityScale(float densityScale) {
-        mDensityScale = densityScale;
-    }
-
-    public void setSnapBackTranslationX(float translationX) {
-        mSnapBackTranslationX = translationX;
-    }
-
-    public void setPagingTouchSlop(float pagingTouchSlop) {
-        mPagingTouchSlop = pagingTouchSlop;
-    }
-
-    public void cancelOngoingDrag() {
-        if (mDragging) {
-            if (mCurrView != null) {
-                mCallback.onDragCancelled(mCurrView);
-                setTranslation(mCurrView, 0);
-                mCallback.onSnapBackCompleted(mCurrView);
-                mCurrView = null;
-            }
-            mDragging = false;
-        }
-    }
-
-    public void resetTranslation(View v) {
-        setTranslation(v, 0);
-    }
-
-    private float getPos(MotionEvent ev) {
-        return mSwipeDirection == X ? ev.getX() : ev.getY();
-    }
-
-    private float getTranslation(View v) {
-        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
-    }
-
-    private float getVelocity(VelocityTracker vt) {
-        return mSwipeDirection == X ? vt.getXVelocity() :
-                vt.getYVelocity();
-    }
-
-    private ObjectAnimator createTranslationAnimation(View v, float newPos) {
-        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
-                mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
-        return anim;
-    }
-
-    private float getPerpendicularVelocity(VelocityTracker vt) {
-        return mSwipeDirection == X ? vt.getYVelocity() :
-                vt.getXVelocity();
-    }
-
-    private void setTranslation(View v, float translate) {
-        if (mSwipeDirection == X) {
-            v.setTranslationX(translate);
-        } else {
-            v.setTranslationY(translate);
-        }
-    }
-
-    private float getSize(View v) {
-        final DisplayMetrics dm = v.getContext().getResources().getDisplayMetrics();
-        return mSwipeDirection == X ? dm.widthPixels : dm.heightPixels;
-    }
-
-    public void setMinAlpha(float minAlpha) {
-        mMinAlpha = minAlpha;
-    }
-
-    float getAlphaForOffset(View view) {
-        float viewSize = getSize(view);
-        final float fadeSize = ALPHA_FADE_END * viewSize;
-        float result = 1.0f;
-        float pos = getTranslation(view);
-        if (pos >= viewSize * ALPHA_FADE_START) {
-            result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
-        } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
-            result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
-        }
-        result = Math.min(result, 1.0f);
-        result = Math.max(result, 0f);
-        return Math.max(mMinAlpha, result);
-    }
-
-    /**
-     * Determines whether the given view has RTL layout.
-     */
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    public static boolean isLayoutRtl(View view) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final int action = ev.getAction();
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mDragging = false;
-                mCurrView = mCallback.getChildAtPosition(ev);
-                mVelocityTracker.clear();
-                if (mCurrView != null) {
-                    mRtl = isLayoutRtl(mCurrView);
-                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
-                    mVelocityTracker.addMovement(ev);
-                    mInitialTouchPos = getPos(ev);
-                } else {
-                    mCanCurrViewBeDimissed = false;
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null) {
-                    mVelocityTracker.addMovement(ev);
-                    float pos = getPos(ev);
-                    float delta = pos - mInitialTouchPos;
-                    if (Math.abs(delta) > mPagingTouchSlop) {
-                        mCallback.onBeginDrag(mCurrView);
-                        mDragging = true;
-                        mInitialTouchPos = pos - getTranslation(mCurrView);
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mDragging = false;
-                mCurrView = null;
-                break;
-        }
-        return mDragging;
-    }
-
-    /**
-     * @param view The view to be dismissed
-     * @param velocity The desired pixels/second speed at which the view should move
-     */
-    private void dismissChild(final View view, float velocity) {
-        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
-        float newPos;
-        if (velocity < 0
-                || (velocity == 0 && getTranslation(view) < 0)
-                // if we use the Menu to dismiss an item in landscape, animate up
-                || (velocity == 0 && getTranslation(view) == 0 && mSwipeDirection == Y)) {
-            newPos = -getSize(view);
-        } else {
-            newPos = getSize(view);
-        }
-        int duration = MAX_ESCAPE_ANIMATION_DURATION;
-        if (velocity != 0) {
-            duration = Math.min(duration,
-                                (int) (Math.abs(newPos - getTranslation(view)) *
-                                        1000f / Math.abs(velocity)));
-        } else {
-            duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
-        }
-
-        ValueAnimator anim = createTranslationAnimation(view, newPos);
-        anim.setInterpolator(sLinearInterpolator);
-        anim.setDuration(duration);
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mCallback.onChildDismissed(view);
-                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
-                    view.setAlpha(1.f);
-                }
-            }
-        });
-        anim.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
-                    view.setAlpha(getAlphaForOffset(view));
-                }
-            }
-        });
-        anim.start();
-    }
-
-    private void snapChild(final View view, float velocity) {
-        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
-        ValueAnimator anim = createTranslationAnimation(view, mSnapBackTranslationX);
-        int duration = SNAP_ANIM_LEN;
-        anim.setDuration(duration);
-        anim.setInterpolator(mLinearOutSlowInInterpolator);
-        anim.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
-                    view.setAlpha(getAlphaForOffset(view));
-                }
-                mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
-            }
-        });
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
-                    view.setAlpha(1.0f);
-                }
-                mCallback.onSnapBackCompleted(view);
-            }
-        });
-        anim.start();
-    }
-
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (!mDragging) {
-            if (!onInterceptTouchEvent(ev)) {
-                return mCanCurrViewBeDimissed;
-            }
-        }
-
-        mVelocityTracker.addMovement(ev);
-        final int action = ev.getAction();
-        switch (action) {
-            case MotionEvent.ACTION_OUTSIDE:
-            case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null) {
-                    float delta = getPos(ev) - mInitialTouchPos;
-                    setSwipeAmount(delta);
-                    mCallback.onSwipeChanged(mCurrView, delta);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                if (mCurrView != null) {
-                    endSwipe(mVelocityTracker);
-                }
-                break;
-        }
-        return true;
-    }
-
-    private void setSwipeAmount(float amount) {
-        // don't let items that can't be dismissed be dragged more than
-        // maxScrollDistance
-        if (CONSTRAIN_SWIPE
-                && (!isValidSwipeDirection(amount) || !mCallback.canChildBeDismissed(mCurrView))) {
-            float size = getSize(mCurrView);
-            float maxScrollDistance = 0.15f * size;
-            if (Math.abs(amount) >= size) {
-                amount = amount > 0 ? maxScrollDistance : -maxScrollDistance;
-            } else {
-                amount = maxScrollDistance * (float) Math.sin((amount/size)*(Math.PI/2));
-            }
-        }
-        setTranslation(mCurrView, amount);
-        if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
-            float alpha = getAlphaForOffset(mCurrView);
-            mCurrView.setAlpha(alpha);
-        }
-    }
-
-    private boolean isValidSwipeDirection(float amount) {
-        if (mSwipeDirection == X) {
-            if (mRtl) {
-                return (amount <= 0) ? mAllowSwipeTowardsEnd : mAllowSwipeTowardsStart;
-            } else {
-                return (amount <= 0) ? mAllowSwipeTowardsStart : mAllowSwipeTowardsEnd;
-            }
-        }
-
-        // Vertical swipes are always valid.
-        return true;
-    }
-
-    private void endSwipe(VelocityTracker velocityTracker) {
-        velocityTracker.computeCurrentVelocity(1000 /* px/sec */);
-        float velocity = getVelocity(velocityTracker);
-        float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
-        float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
-        float translation = getTranslation(mCurrView);
-        // Decide whether to dismiss the current view
-        boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
-                Math.abs(translation) > 0.6 * getSize(mCurrView);
-        boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
-                (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
-                (velocity > 0) == (translation > 0);
-
-        boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
-                && isValidSwipeDirection(translation)
-                && (childSwipedFastEnough || childSwipedFarEnough);
-
-        if (dismissChild) {
-            // flingadingy
-            dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
-        } else {
-            // snappity
-            mCallback.onDragCancelled(mCurrView);
-            snapChild(mCurrView, velocity);
-        }
-    }
-
-    public interface Callback {
-        View getChildAtPosition(MotionEvent ev);
-
-        boolean canChildBeDismissed(View v);
-
-        void onBeginDrag(View v);
-
-        void onSwipeChanged(View v, float delta);
-
-        void onChildDismissed(View v);
-
-        void onSnapBackCompleted(View v);
-
-        void onDragCancelled(View v);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index f84eb53..9618f212d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -22,9 +22,6 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 
@@ -64,8 +61,6 @@
      */
     public void prepareEnterRecentsAnimation(boolean hasStatusBarScrim, boolean animateStatusBarScrim,
             boolean hasNavBarScrim, boolean animateNavBarScrim) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
         mHasNavBarScrim = hasStatusBarScrim;
         mShouldAnimateStatusBarScrim = animateStatusBarScrim;
         mHasStatusBarScrim = hasNavBarScrim;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
new file mode 100644
index 0000000..2930f4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.util.List;
+
+/**
+ * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
+ * but not the contents of the {@link TaskView}s.
+ */
+public class TaskStackAnimationHelper {
+
+    /**
+     * Callbacks from the helper to coordinate view-content animations with view animations.
+     */
+    public interface Callbacks {
+        /**
+         * Callback to prepare for the start animation for the launch target {@link TaskView}.
+         */
+        void onPrepareLaunchTargetForEnterAnimation();
+
+        /**
+         * Callback to start the animation for the launch target {@link TaskView}.
+         */
+        void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
+                ReferenceCountedTrigger postAnimationTrigger);
+
+        /**
+         * Callback to start the animation for the launch target {@link TaskView} when it is
+         * launched from Recents.
+         */
+        void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
+                ReferenceCountedTrigger postAnimationTrigger);
+    }
+
+    private TaskStackView mStackView;
+
+    private Interpolator mFastOutSlowInInterpolator;
+    private Interpolator mFastOutLinearInInterpolator;
+    private Interpolator mQuintOutInterpolator;
+
+    private TaskViewTransform mTmpTransform = new TaskViewTransform();
+
+    public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
+        mStackView = stackView;
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
+        mQuintOutInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.decelerate_quint);
+    }
+
+    /**
+     * Prepares the stack views and puts them in their initial animation state while visible, before
+     * the in-app enter animations start (after the window-transition completes).
+     */
+    public void prepareForEnterAnimation() {
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        Resources res = mStackView.getResources();
+
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+        TaskStack stack = mStackView.getStack();
+        Task launchTargetTask = stack.getLaunchTarget();
+
+        // Break early if there are no tasks
+        if (stack.getTaskCount() == 0) {
+            return;
+        }
+
+        int offscreenY = stackLayout.mStackRect.bottom;
+        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+                R.dimen.recents_task_view_affiliate_group_enter_offset);
+
+        // Prepare each of the task views for their enter animation from front to back
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+            boolean currentTaskOccludesLaunchTarget = (launchTargetTask != null &&
+                    launchTargetTask.group.isTaskAboveTask(task, launchTargetTask));
+            boolean hideTask = (launchTargetTask != null &&
+                    launchTargetTask.isFreeformTask() && task.isFreeformTask());
+
+            // Get the current transform for the task, which will be used to position it offscreen
+            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                    null);
+
+            if (hideTask) {
+                tv.setVisibility(View.INVISIBLE);
+            } else if (launchState.launchedHasConfigurationChanged) {
+                // Just load the views as-is
+            } else if (launchState.launchedFromAppWithThumbnail) {
+                if (task.isLaunchTarget) {
+                    tv.onPrepareLaunchTargetForEnterAnimation();
+                } else if (currentTaskOccludesLaunchTarget) {
+                    // Move the task view slightly lower so we can animate it in
+                    RectF bounds = new RectF(mTmpTransform.rect);
+                    bounds.offset(0, taskViewAffiliateGroupEnterOffset);
+                    tv.setAlpha(0f);
+                    tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top,
+                            (int) bounds.right, (int) bounds.bottom);
+                }
+            } else if (launchState.launchedFromHome) {
+                // Move the task view off screen (below) so we can animate it in
+                RectF bounds = new RectF(mTmpTransform.rect);
+                bounds.offset(0, offscreenY);
+                tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
+                        (int) bounds.bottom);
+            }
+        }
+    }
+
+    /**
+     * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
+     * depending on how Recents was triggered.
+     */
+    public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
+        RecentsConfiguration config = Recents.getConfiguration();
+        RecentsActivityLaunchState launchState = config.getLaunchState();
+        Resources res = mStackView.getResources();
+
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+        TaskStack stack = mStackView.getStack();
+        Task launchTargetTask = stack.getLaunchTarget();
+
+        // Break early if there are no tasks
+        if (stack.getTaskCount() == 0) {
+            return;
+        }
+
+        int taskViewEnterFromAppDuration = res.getInteger(
+                R.integer.recents_task_enter_from_app_duration);
+        int taskViewEnterFromHomeDuration = res.getInteger(
+                R.integer.recents_task_enter_from_home_duration);
+        int taskViewEnterFromHomeStaggerDelay = res.getInteger(
+                R.integer.recents_task_enter_from_home_stagger_delay);
+
+        // Create enter animations for each of the views from front to back
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+            boolean currentTaskOccludesLaunchTarget = false;
+            if (launchTargetTask != null) {
+                currentTaskOccludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
+                        launchTargetTask);
+            }
+
+            // Get the current transform for the task, which will be updated to the final transform
+            // to animate to depending on how recents was invoked
+            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                    null);
+
+            if (launchState.launchedFromAppWithThumbnail) {
+                if (task.isLaunchTarget) {
+                    tv.onStartLaunchTargetEnterAnimation(taskViewEnterFromAppDuration,
+                            mStackView.mScreenPinningEnabled, postAnimationTrigger);
+                } else {
+                    // Animate the task up if it was occluding the launch target
+                    if (currentTaskOccludesLaunchTarget) {
+                        TaskViewAnimation taskAnimation = new TaskViewAnimation(
+                                taskViewEnterFromAppDuration, PhoneStatusBar.ALPHA_IN,
+                                postAnimationTrigger.decrementOnAnimationEnd());
+                        postAnimationTrigger.increment();
+                        mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+                    }
+                }
+
+            } else if (launchState.launchedFromHome) {
+                // Animate the tasks up
+                int frontIndex = (taskViewCount - i - 1);
+                int delay = frontIndex * taskViewEnterFromHomeStaggerDelay;
+                int duration = taskViewEnterFromHomeDuration +
+                        frontIndex * taskViewEnterFromHomeStaggerDelay;
+
+                TaskViewAnimation taskAnimation = new TaskViewAnimation(delay,
+                        duration, mQuintOutInterpolator,
+                        postAnimationTrigger.decrementOnAnimationEnd());
+                postAnimationTrigger.increment();
+                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+            }
+        }
+    }
+
+    /**
+     * Starts an in-app animation to hide all the task views so that we can transition back home.
+     */
+    public void startExitToHomeAnimation(boolean animated,
+            ReferenceCountedTrigger postAnimationTrigger) {
+        Resources res = mStackView.getResources();
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+        TaskStack stack = mStackView.getStack();
+
+        // Break early if there are no tasks
+        if (stack.getTaskCount() == 0) {
+            return;
+        }
+
+        int offscreenY = stackLayout.mStackRect.bottom;
+        int taskViewExitToHomeDuration = res.getInteger(
+                R.integer.recents_task_exit_to_home_duration);
+
+        // Create the animations for each of the tasks
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+            TaskViewAnimation taskAnimation = new TaskViewAnimation(
+                    animated ? taskViewExitToHomeDuration : 0, mFastOutLinearInInterpolator,
+                    postAnimationTrigger.decrementOnAnimationEnd());
+            postAnimationTrigger.increment();
+
+            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                    null);
+            mTmpTransform.rect.offset(0, offscreenY);
+            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+        }
+    }
+
+    /**
+     * Starts the animation for the launching task view, hiding any tasks that might occlude the
+     * window transition for the launching task.
+     */
+    public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        Resources res = mStackView.getResources();
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+        int taskViewExitToAppDuration = res.getInteger(
+                R.integer.recents_task_exit_to_app_duration);
+        int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
+                R.dimen.recents_task_view_affiliate_group_enter_offset);
+
+        Task launchingTask = launchingTaskView.getTask();
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+            boolean currentTaskOccludesLaunchTarget = (launchingTask != null &&
+                    launchingTask.group.isTaskAboveTask(task, launchingTask));
+
+            if (tv == launchingTaskView) {
+                tv.setClipViewInStack(false);
+                tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
+                        screenPinningRequested, postAnimationTrigger);
+            } else if (currentTaskOccludesLaunchTarget) {
+                // Animate this task out of view
+                TaskViewAnimation taskAnimation = new TaskViewAnimation(
+                        taskViewExitToAppDuration, mFastOutLinearInInterpolator,
+                        postAnimationTrigger.decrementOnAnimationEnd());
+                postAnimationTrigger.increment();
+
+                stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                        null);
+                mTmpTransform.alpha = 0f;
+                mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
+                mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+            }
+        }
+    }
+
+    /**
+     * Starts the delete animation for the specified {@link TaskView}.
+     */
+    public void startDeleteTaskAnimation(Task deleteTask, final TaskView deleteTaskView,
+            final ReferenceCountedTrigger postAnimationTrigger) {
+        Resources res = mStackView.getResources();
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+        int taskViewRemoveAnimDuration = res.getInteger(
+                R.integer.recents_animate_task_view_remove_duration);
+        int taskViewRemoveAnimTranslationXPx = res.getDimensionPixelSize(
+                R.dimen.recents_task_view_remove_anim_translation_x);
+
+        // Disabling clipping with the stack while the view is animating away
+        deleteTaskView.setClipViewInStack(false);
+
+        // Compose the new animation and transform and star the animation
+        TaskViewAnimation taskAnimation = new TaskViewAnimation(taskViewRemoveAnimDuration,
+                PhoneStatusBar.ALPHA_OUT, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                postAnimationTrigger.decrement();
+
+                // Re-enable clipping with the stack (we will reuse this view)
+                deleteTaskView.setClipViewInStack(true);
+            }
+        });
+        postAnimationTrigger.increment();
+
+        stackLayout.getStackTransform(deleteTask, stackScroller.getStackScroll(), mTmpTransform,
+                null);
+        mTmpTransform.alpha = 0f;
+        mTmpTransform.rect.offset(taskViewRemoveAnimTranslationXPx, 0);
+        mStackView.updateTaskViewToTransform(deleteTaskView, mTmpTransform, taskAnimation);
+    }
+
+    /**
+     * Starts the animation to hide the {@link TaskView}s when the history is shown.  The history
+     * view's animation will be deferred until all the {@link TaskView}s are finished animating.
+     */
+    public void startShowHistoryAnimation(ReferenceCountedTrigger postAnimationTrigger) {
+        Resources res = mStackView.getResources();
+        TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+        int historyTransitionDuration = res.getInteger(
+                R.integer.recents_history_transition_duration);
+
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            TaskView tv = taskViews.get(i);
+            Task task = tv.getTask();
+            TaskViewAnimation taskAnimation = new TaskViewAnimation(
+                    historyTransitionDuration, PhoneStatusBar.ALPHA_OUT,
+                    postAnimationTrigger.decrementOnAnimationEnd());
+            postAnimationTrigger.increment();
+
+            stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
+                    null);
+            mTmpTransform.alpha = 0f;
+            mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+        }
+    }
+
+    /**
+     * Starts the animation to show the {@link TaskView}s when the history is hidden.  The
+     * {@link TaskView} animations will be deferred until the history view has been animated away.
+     */
+    public void startHideHistoryAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
+        final Resources res = mStackView.getResources();
+        final TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+        final TaskStackViewScroller stackScroller = mStackView.getScroller();
+
+        final int historyTransitionDuration = res.getInteger(
+                R.integer.recents_history_transition_duration);
+
+        List<TaskView> taskViews = mStackView.getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            final TaskView tv = taskViews.get(i);
+            postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    TaskViewAnimation taskAnimation = new TaskViewAnimation(
+                            historyTransitionDuration, PhoneStatusBar.ALPHA_IN);
+                    stackLayout.getStackTransform(tv.getTask(), stackScroller.getStackScroll(),
+                            mTmpTransform, null);
+                    mTmpTransform.alpha = 1f;
+                    mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+                }
+            });
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 10df156..2fa99ce 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -21,8 +21,9 @@
 import android.content.res.Resources;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.FloatProperty;
-import android.util.Log;
 import android.util.Property;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -38,7 +39,6 @@
 import com.android.systemui.recents.model.TaskStack;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 
 /**
  * Used to describe a visible range that can be normalized to [0, 1].
@@ -103,9 +103,6 @@
  */
 public class TaskStackLayoutAlgorithm {
 
-    private static final String TAG = "TaskStackViewLayoutAlgorithm";
-    private static final boolean DEBUG = false;
-
     // The scale factor to apply to the user movement in the stack to unfocus it
     private static final float UNFOCUS_MULTIPLIER = 0.8f;
 
@@ -130,7 +127,7 @@
          *                          allocate to the freeform workspace
          * @param freeformBackgroundAlpha the background alpha for the freeform workspace
          */
-        StackState(float freeformHeightPct, int freeformBackgroundAlpha) {
+        private StackState(float freeformHeightPct, int freeformBackgroundAlpha) {
             this.freeformHeightPct = freeformHeightPct;
             this.freeformBackgroundAlpha = freeformBackgroundAlpha;
         }
@@ -141,9 +138,8 @@
         public static StackState getStackStateForStack(TaskStack stack) {
             SystemServicesProxy ssp = Recents.getSystemServices();
             boolean hasFreeformWorkspaces = ssp.hasFreeformWorkspaceSupport();
-            int taskCount = stack.getStackTaskCount();
-            int freeformCount = stack.getStackTaskFreeformCount();
-            int stackCount = taskCount - freeformCount;
+            int freeformCount = stack.getFreeformTaskCount();
+            int stackCount = stack.getStackTaskCount();
             if (hasFreeformWorkspaces && stackCount > 0 && freeformCount > 0) {
                 return SPLIT;
             } else if (hasFreeformWorkspaces && freeformCount > 0) {
@@ -212,7 +208,6 @@
     }
 
     Context mContext;
-    private TaskStackView mStackView;
     private Interpolator mLinearOutSlowInInterpolator;
     private StackState mState = StackState.SPLIT;
 
@@ -275,14 +270,17 @@
     int mMaxTranslationZ;
 
     // Optimization, allows for quick lookup of task -> index
-    private HashMap<Task.TaskKey, Integer> mTaskIndexMap = new HashMap<>();
+    private ArrayMap<Task.TaskKey, Integer> mTaskIndexMap = new ArrayMap<>();
 
     // The freeform workspace layout
     FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
 
-    public TaskStackLayoutAlgorithm(Context context, TaskStackView stackView) {
+    // The transform to place TaskViews at the front and back of the stack respectively
+    TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
+    TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
+
+    public TaskStackLayoutAlgorithm(Context context) {
         Resources res = context.getResources();
-        mStackView = stackView;
 
         mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
                 res.getFloat(R.integer.recents_layout_focused_range_max));
@@ -318,7 +316,7 @@
      */
     public void setFocusState(float focusState) {
         mFocusState = focusState;
-        mStackView.requestSynchronizeStackViewsWithModel();
+        updateFrontBackTransforms();
     }
 
     /**
@@ -333,7 +331,6 @@
      * including the search bar.
      */
     public void initialize(Rect taskStackBounds, StackState state) {
-        RecentsDebugFlags debugFlags = Recents.getDebugFlags();
         RecentsConfiguration config = Recents.getConfiguration();
         int widthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
         int heightPadding = mContext.getResources().getDimensionPixelSize(
@@ -369,21 +366,14 @@
         mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
         mFocusedCurve = constructFocusedCurve();
         mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
-
-        if (DEBUG) {
-            Log.d(TAG, "initialize");
-            Log.d(TAG, "\tmFreeformRect: " + mFreeformRect);
-            Log.d(TAG, "\tmStackRect: " + mStackRect);
-            Log.d(TAG, "\tmTaskRect: " + mTaskRect);
-            Log.d(TAG, "\tmSystemInsets: " + mSystemInsets);
-        }
+        updateFrontBackTransforms();
     }
 
     /**
      * Computes the minimum and maximum scroll progress values and the progress values for each task
      * in the stack.
      */
-    void update(TaskStack stack) {
+    void update(TaskStack stack, ArraySet<Task> ignoreTasksSet) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Clear the progress map
@@ -403,6 +393,9 @@
         ArrayList<Task> stackTasks = new ArrayList<>();
         for (int i = 0; i < tasks.size(); i++) {
             Task task = tasks.get(i);
+            if (ignoreTasksSet.contains(task)) {
+                continue;
+            }
             if (task.isFreeformTask()) {
                 freeformTasks.add(task);
             } else {
@@ -415,6 +408,7 @@
         // Put each of the tasks in the progress map at a fixed index (does not need to actually
         // map to a scroll position, just by index)
         int taskCount = stackTasks.size();
+        mTaskIndexMap.ensureCapacity(taskCount);
         for (int i = 0; i < taskCount; i++) {
             Task task = stackTasks.get(i);
             mTaskIndexMap.put(task.key, i);
@@ -456,13 +450,6 @@
                 mInitialScrollP = (mNumStackTasks - 1) - mUnfocusedRange.getAbsoluteX(normX);
             }
         }
-
-        if (DEBUG) {
-            Log.d(TAG, "mNumStackTasks: " + mNumStackTasks);
-            Log.d(TAG, "mNumFreeformTasks: " + mNumFreeformTasks);
-            Log.d(TAG, "mMinScrollP: " + mMinScrollP);
-            Log.d(TAG, "mMaxScrollP: " + mMaxScrollP);
-        }
     }
 
     /**
@@ -472,7 +459,7 @@
         Utilities.cancelAnimationWithoutCallbacks(mFocusStateAnimator);
         if (mFocusState > STATE_UNFOCUSED) {
             float delta = (float) yMovement / (UNFOCUS_MULTIPLIER * mStackRect.height());
-            mFocusState -= Math.min(mFocusState, Math.abs(delta));
+            setFocusState(mFocusState - Math.min(mFocusState, Math.abs(delta)));
         }
     }
 
@@ -498,27 +485,23 @@
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         RecentsDebugFlags debugFlags = Recents.getDebugFlags();
         if (launchState.launchedWithAltTab || debugFlags.isInitialStatePaging()) {
-            return 1f;
+            return STATE_FOCUSED;
         }
-        return 0f;
+        return STATE_UNFOCUSED;
     }
 
     /**
-     * Returns the task progress that would put the task just off the back of the stack.
+     * Returns the TaskViewTransform that would put the task just off the back of the stack.
      */
-    public float getStackBackTaskProgress(float stackScroll) {
-        float min = mUnfocusedRange.relativeMin +
-                mFocusState * (mFocusedRange.relativeMin - mUnfocusedRange.relativeMin);
-        return stackScroll + min;
+    public TaskViewTransform getBackOfStackTransform() {
+        return mBackOfStackTransform;
     }
 
     /**
-     * Returns the task progress that would put the task just off the front of the stack.
+     * Returns the TaskViewTransform that would put the task just off the front of the stack.
      */
-    public float getStackFrontTaskProgress(float stackScroll) {
-        float max = mUnfocusedRange.relativeMax +
-                mFocusState * (mFocusedRange.relativeMax - mUnfocusedRange.relativeMax);
-        return stackScroll + max;
+    public TaskViewTransform getFrontOfStackTransform() {
+        return mFrontOfStackTransform;
     }
 
     /**
@@ -613,15 +596,8 @@
                 transformOut.reset();
                 return transformOut;
             }
-            getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
+            return getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
                     frontTransform);
-            if (task.thumbnail != null) {
-                transformOut.thumbnailScale = (float) mTaskRect.width() / task.thumbnail.getWidth();
-            }
-            if (DEBUG) {
-                Log.d(TAG, "getTransform: " + task.key + ", " + transformOut);
-            }
-            return transformOut;
         }
     }
 
@@ -673,7 +649,11 @@
             y += (mStackRect.top - mTaskRect.top);
             z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ,
                     mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ))));
-            relP = unfocusedP;
+            if (mNumStackTasks == 1) {
+                relP = 1f;
+            } else {
+                relP = Math.min(mMaxScrollP, unfocusedP);
+            }
         }
 
         // Fill out the transform
@@ -685,7 +665,6 @@
         Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
         transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
                 (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
-        transformOut.thumbnailScale = 1f;
         transformOut.p = relP;
         return transformOut;
     }
@@ -770,4 +749,23 @@
         p.cubicTo(0.5f, 1f - peekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
         return p;
     }
+
+    /**
+     * Updates the current transforms that would put a TaskView at the front and back of the stack.
+     */
+    private void updateFrontBackTransforms() {
+        // Return early if we have not yet initialized
+        if (mStackRect.isEmpty()) {
+            return;
+        }
+
+        float min = mUnfocusedRange.relativeMin +
+                mFocusState * (mFocusedRange.relativeMin - mUnfocusedRange.relativeMin);
+        float max = mUnfocusedRange.relativeMax +
+                mFocusState * (mFocusedRange.relativeMax - mUnfocusedRange.relativeMax);
+        getStackTransform(min, 0f, mBackOfStackTransform, null);
+        getStackTransform(max, 0f, mFrontOfStackTransform, null);
+        mBackOfStackTransform.visible = true;
+        mFrontOfStackTransform.visible = true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 830d607..713cfc3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,14 +23,13 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
 import android.os.Parcelable;
-import android.util.IntProperty;
-import android.util.Log;
-import android.util.Property;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -46,19 +45,22 @@
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
 import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.HideHistoryEvent;
 import com.android.systemui.recents.events.activity.IterateRecentsEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
 import com.android.systemui.recents.events.activity.ShowHistoryEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
 import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
 import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
 import com.android.systemui.recents.events.ui.UserInteractionEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
@@ -76,8 +78,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -90,9 +90,6 @@
         TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
         ViewPool.ViewPoolConsumer<TaskView, Task> {
 
-    private final static String TAG = "TaskStackView";
-    private final static boolean DEBUG = false;
-
     private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
     private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
             "saved_instance_state_layout_focused_state";
@@ -104,52 +101,46 @@
     private static final float HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
 
     private static final int DEFAULT_SYNC_STACK_DURATION = 200;
-
-    public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
-            new IntProperty<Drawable>("drawableAlpha") {
-                @Override
-                public void setValue(Drawable object, int alpha) {
-                    object.setAlpha(alpha);
-                }
-
-                @Override
-                public Integer get(Drawable object) {
-                    return object.getAlpha();
-                }
-            };
+    private static final int DRAG_SCALE_DURATION = 175;
+    private static final float DRAG_SCALE_FACTOR = 1.05f;
 
     TaskStack mStack;
     TaskStackLayoutAlgorithm mLayoutAlgorithm;
     TaskStackViewScroller mStackScroller;
     TaskStackViewTouchHandler mTouchHandler;
+    TaskStackAnimationHelper mAnimationHelper;
     GradientDrawable mFreeformWorkspaceBackground;
     ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
     ViewPool<TaskView, Task> mViewPool;
+    boolean mStartTimerIndicator;
+
+    ArrayList<TaskView> mTaskViews = new ArrayList<>();
     ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
+    TaskViewAnimation mDeferredTaskViewUpdateAnimation = null;
+
     DozeTrigger mUIDozeTrigger;
     Task mFocusedTask;
-    // Optimizations
-    int mStackViewsAnimationDuration;
+
     int mTaskCornerRadiusPx;
-    boolean mStackViewsDirty = true;
-    boolean mStackViewsClipDirty = true;
+    private int mDividerSize;
+
+    boolean mTaskViewsClipDirty = true;
     boolean mAwaitingFirstLayout = true;
     boolean mEnterAnimationComplete = false;
-    boolean mStartEnterAnimationRequestedAfterLayout;
-    ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
+    boolean mTouchExplorationEnabled;
+    boolean mScreenPinningEnabled;
 
-    Rect mTaskStackBounds = new Rect();
+    // The stable stack bounds are the full bounds that we were measured with from RecentsView
+    Rect mStableStackBounds = new Rect();
+    // The current stack bounds are dynamic and may change as the user drags and drops
+    Rect mStackBounds = new Rect();
     int[] mTmpVisibleRange = new int[2];
     Rect mTmpRect = new Rect();
-    RectF mTmpTaskRect = new RectF();
-    TaskViewTransform mTmpStackBackTransform = new TaskViewTransform();
-    TaskViewTransform mTmpStackFrontTransform = new TaskViewTransform();
-    HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
-    ArrayList<TaskView> mTaskViews = new ArrayList<>();
-    List<TaskView> mImmutableTaskViews = new ArrayList<>();
+    ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
+    ArraySet<Task> mTmpTaskSet = new ArraySet<>();
     List<TaskView> mTmpTaskViews = new ArrayList<>();
+    TaskViewTransform mTmpTransform = new TaskViewTransform();
     LayoutInflater mInflater;
-    boolean mTouchExplorationEnabled;
 
     Interpolator mFastOutSlowInInterpolator;
 
@@ -158,22 +149,35 @@
             new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
-                    requestUpdateStackViewsClip();
+                    if (!mTaskViewsClipDirty) {
+                        mTaskViewsClipDirty = true;
+                        invalidate();
+                    }
                 }
             };
 
     // The drop targets for a task drag
     private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
         @Override
-        public boolean acceptsDrop(int x, int y, int width, int height) {
-            return mLayoutAlgorithm.mFreeformRect.contains(x, y);
+        public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+            // This drop target has a fixed bounds and should be checked last, so just fall through
+            // if it is the current target
+            if (!isCurrentTarget) {
+                return mLayoutAlgorithm.mFreeformRect.contains(x, y);
+            }
+            return false;
         }
     };
 
     private DropTarget mStackDropTarget = new DropTarget() {
         @Override
-        public boolean acceptsDrop(int x, int y, int width, int height) {
-            return mLayoutAlgorithm.mStackRect.contains(x, y);
+        public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+            // This drop target has a fixed bounds and should be checked last, so just fall through
+            // if it is the current target
+            if (!isCurrentTarget) {
+                return mLayoutAlgorithm.mStackRect.contains(x, y);
+            }
+            return false;
         }
     };
 
@@ -186,14 +190,16 @@
         setStack(stack);
         mViewPool = new ViewPool<>(context, this);
         mInflater = LayoutInflater.from(context);
-        mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
+        mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context);
         mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm);
         mStackScroller.setCallbacks(this);
         mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
+        mAnimationHelper = new TaskStackAnimationHelper(context, this);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
         mTaskCornerRadiusPx = res.getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius);
+        mDividerSize = ssp.getDockedDividerSize(context);
 
         int taskBarDismissDozeDelaySeconds = getResources().getInteger(
                 R.integer.recents_task_bar_dismiss_delay_seconds);
@@ -222,9 +228,8 @@
 
     @Override
     protected void onAttachedToWindow() {
-        SystemServicesProxy ssp = Recents.getSystemServices();
-        mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
         EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        readSystemFlags();
         super.onAttachedToWindow();
     }
 
@@ -260,12 +265,11 @@
                 mTaskViews.add((TaskView) v);
             }
         }
-        mImmutableTaskViews = Collections.unmodifiableList(mTaskViews);
     }
 
     /** Gets the list of task views */
     List<TaskView> getTaskViews() {
-        return mImmutableTaskViews;
+        return mTaskViews;
     }
 
     /**
@@ -324,44 +328,17 @@
 
         // Reset the stack state
         mStack.reset();
-        mStackViewsDirty = true;
-        mStackViewsClipDirty = true;
+        mTaskViewsClipDirty = true;
         mAwaitingFirstLayout = true;
         mEnterAnimationComplete = false;
-        if (mUIDozeTrigger != null) {
-            mUIDozeTrigger.stopDozing();
-            mUIDozeTrigger.resetTrigger();
-        }
+        mUIDozeTrigger.stopDozing();
+        mUIDozeTrigger.resetTrigger();
         mStackScroller.reset();
         mLayoutAlgorithm.reset();
+        readSystemFlags();
         requestLayout();
     }
 
-    /** Requests that the views be synchronized with the model */
-    void requestSynchronizeStackViewsWithModel() {
-        requestSynchronizeStackViewsWithModel(0);
-    }
-    void requestSynchronizeStackViewsWithModel(int duration) {
-        if (!mStackViewsDirty) {
-            invalidate();
-            mStackViewsDirty = true;
-        }
-        if (mAwaitingFirstLayout) {
-            // Skip the animation if we are awaiting first layout
-            mStackViewsAnimationDuration = 0;
-        } else {
-            mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
-        }
-    }
-
-    /** Requests that the views clipping be updated. */
-    void requestUpdateStackViewsClip() {
-        if (!mStackViewsClipDirty) {
-            invalidate();
-            mStackViewsClipDirty = true;
-        }
-    }
-
     /** Returns the stack algorithm for this task stack. */
     public TaskStackLayoutAlgorithm getStackAlgorithm() {
         return mLayoutAlgorithm;
@@ -372,9 +349,8 @@
      * This call ignores freeform tasks.
      */
     private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
-                                       ArrayList<Task> tasks,
-                                       float stackScroll,
-                                       int[] visibleRangeOut) {
+            ArrayList<Task> tasks, float stackScroll,
+            int[] visibleRangeOut, ArraySet<Task> ignoreTasksSet) {
         int taskTransformCount = taskTransforms.size();
         int taskCount = tasks.size();
         int frontMostVisibleIndex = -1;
@@ -395,15 +371,19 @@
         TaskViewTransform frontTransform = null;
         for (int i = taskCount - 1; i >= 0; i--) {
             Task task = tasks.get(i);
-            if (task.isFreeformTask()) {
+            if (ignoreTasksSet.contains(task)) {
                 continue;
             }
 
             TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
                     taskTransforms.get(i), frontTransform);
-            if (DEBUG) {
-                Log.d(TAG, "updateStackTransform: " + i + ", " + transform.visible);
+
+            // For freeform tasks, only calculate the stack transform and skip the calculation of
+            // the visible stack indices
+            if (task.isFreeformTask()) {
+                continue;
             }
+
             if (transform.visible) {
                 if (frontMostVisibleIndex < 0) {
                     frontMostVisibleIndex = i;
@@ -429,144 +409,177 @@
         return frontMostVisibleIndex != -1 && backMostVisibleIndex != -1;
     }
 
-    /** Synchronizes the views with the model */
-    boolean synchronizeStackViewsWithModel() {
-        if (mStackViewsDirty) {
-            // Get all the task transforms
-            ArrayList<Task> tasks = mStack.getStackTasks();
-            float stackScroll = mStackScroller.getStackScroll();
-            int[] visibleStackRange = mTmpVisibleRange;
-            boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
-                    stackScroll, visibleStackRange);
-            boolean hasStackBackTransform = false;
-            boolean hasStackFrontTransform = false;
-            if (DEBUG) {
-                Log.d(TAG, "visibleRange: " + visibleStackRange[0] + " to " + visibleStackRange[1]);
+    /**
+     * Updates the children {@link TaskView}s to match the tasks in the current {@link TaskStack}.
+     * This call does not update the {@link TaskView}s to their position in the layout except when
+     * they are initially picked up from the pool, when they will be placed in a suitable initial
+     * position.
+     */
+    private void bindTaskViewsWithStack(ArraySet<Task> ignoreTasksSet) {
+        final float stackScroll = mStackScroller.getStackScroll();
+        final int[] visibleStackRange = mTmpVisibleRange;
+
+        // Get all the task transforms
+        final ArrayList<Task> tasks = mStack.getStackTasks();
+        final boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms,
+                tasks, stackScroll, visibleStackRange, ignoreTasksSet);
+
+        // Return all the invisible children to the pool
+        final List<TaskView> taskViews = getTaskViews();
+        final int taskViewCount = taskViews.size();
+        int lastFocusedTaskIndex = -1;
+        mTmpTaskViewMap.clear();
+        mTmpTaskViewMap.ensureCapacity(tasks.size());
+        for (int i = taskViewCount - 1; i >= 0; i--) {
+            final TaskView tv = taskViews.get(i);
+            final Task task = tv.getTask();
+            final int taskIndex = mStack.indexOfStackTask(task);
+
+            // Skip ignored tasks
+            if (ignoreTasksSet.contains(task)) {
+                continue;
             }
 
-            // Return all the invisible children to the pool
-            mTmpTaskViewMap.clear();
-            List<TaskView> taskViews = getTaskViews();
-            int lastFocusedTaskIndex = -1;
-            int taskViewCount = taskViews.size();
-            for (int i = taskViewCount - 1; i >= 0; i--) {
-                TaskView tv = taskViews.get(i);
-                Task task = tv.getTask();
-                int taskIndex = mStack.indexOfStackTask(task);
-                if (task.isFreeformTask() ||
-                        visibleStackRange[1] <= taskIndex && taskIndex <= visibleStackRange[0]) {
-                    mTmpTaskViewMap.put(task, tv);
-                } else {
-                    if (mTouchExplorationEnabled) {
-                        lastFocusedTaskIndex = taskIndex;
-                        resetFocusedTask(task);
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "returning to pool: " + task.key);
-                    }
-                    mViewPool.returnViewToPool(tv);
+            if (task.isFreeformTask() ||
+                    visibleStackRange[1] <= taskIndex && taskIndex <= visibleStackRange[0]) {
+                mTmpTaskViewMap.put(task.key, tv);
+            } else {
+                if (mTouchExplorationEnabled) {
+                    lastFocusedTaskIndex = taskIndex;
+                    resetFocusedTask(task);
                 }
+                mViewPool.returnViewToPool(tv);
             }
-
-            // Pick up all the freeform tasks
-            int firstVisStackIndex = isValidVisibleStackRange ? visibleStackRange[0] : 0;
-            for (int i = mStack.getStackTaskCount() - 1; i >= firstVisStackIndex; i--) {
-                Task task = tasks.get(i);
-                if (!task.isFreeformTask()) {
-                    continue;
-                }
-                TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
-                        mCurrentTaskTransforms.get(i), null);
-                TaskView tv = mTmpTaskViewMap.get(task);
-                if (tv == null) {
-                    if (DEBUG) {
-                        Log.d(TAG, "picking up from pool: " + task.key);
-                    }
-                    tv = mViewPool.pickUpViewFromPool(task, task);
-                } else {
-                    // Reattach it in the right z order
-                    int taskIndex = mStack.indexOfStackTask(task);
-                    int insertIndex = findTaskViewInsertIndex(task, taskIndex);
-                    if (insertIndex != getTaskViews().indexOf(tv)){
-                        detachViewFromParent(tv);
-                        attachViewToParent(tv, insertIndex, tv.getLayoutParams());
-                    }
-                }
-
-                // Animate the task into place
-                tv.updateViewPropertiesToTaskTransform(transform, 0,
-                        mStackViewsAnimationDuration, mFastOutSlowInInterpolator,
-                        mRequestUpdateClippingListener);
-
-                // Update the task views list after adding the new task view
-                updateTaskViewsList();
-            }
-
-            // Pick up all the newly visible children and update all the existing children
-            for (int i = visibleStackRange[0];
-                    isValidVisibleStackRange && i >= visibleStackRange[1]; i--) {
-                Task task = tasks.get(i);
-                TaskViewTransform transform = mCurrentTaskTransforms.get(i);
-                TaskView tv = mTmpTaskViewMap.get(task);
-
-                if (tv == null) {
-                    tv = mViewPool.pickUpViewFromPool(task, task);
-                    if (mStackViewsAnimationDuration > 0) {
-                        // For items in the list, put them in start animating them from the
-                        // approriate ends of the list where they are expected to appear
-                        if (Float.compare(transform.p, 0f) <= 0) {
-                            if (!hasStackBackTransform) {
-                                hasStackBackTransform = true;
-                                mLayoutAlgorithm.getStackTransform(
-                                        mLayoutAlgorithm.getStackBackTaskProgress(0f), 0f,
-                                        mTmpStackBackTransform, null);
-                            }
-                            tv.updateViewPropertiesToTaskTransform(mTmpStackBackTransform, 0, 0,
-                                    mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
-                        } else {
-                            if (!hasStackFrontTransform) {
-                                hasStackFrontTransform = true;
-                                mLayoutAlgorithm.getStackTransform(
-                                        mLayoutAlgorithm.getStackFrontTaskProgress(0f), 0f,
-                                        mTmpStackFrontTransform, null);
-                            }
-                            tv.updateViewPropertiesToTaskTransform(mTmpStackFrontTransform, 0, 0,
-                                    mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
-                        }
-                    }
-                }
-
-                // Animate the task into place, the clip for stack tasks will be calculated in
-                // clipTaskViews()
-                tv.updateViewPropertiesToTaskTransform(transform,
-                        tv.getViewBounds().getClipBottom(), mStackViewsAnimationDuration,
-                        mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
-            }
-
-            // Update the focus if the previous focused task was returned to the view pool
-            if (lastFocusedTaskIndex != -1) {
-                if (lastFocusedTaskIndex < visibleStackRange[1]) {
-                    setFocusedTask(visibleStackRange[1], false /* animated */,
-                            true /* requestViewFocus */);
-                } else {
-                    setFocusedTask(visibleStackRange[0], false /* animated */,
-                            true /* requestViewFocus */);
-                }
-            }
-
-            // Reset the request-synchronize params
-            mStackViewsAnimationDuration = 0;
-            mStackViewsDirty = false;
-            mStackViewsClipDirty = true;
-            return true;
         }
-        return false;
+
+        // Pick up all the newly visible children
+        int lastVisStackIndex = isValidVisibleStackRange ? visibleStackRange[1] : 0;
+        for (int i = mStack.getTaskCount() - 1; i >= lastVisStackIndex; i--) {
+            final Task task = tasks.get(i);
+            final TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+
+            // Skip ignored tasks
+            if (ignoreTasksSet.contains(task)) {
+                continue;
+            }
+
+            // Skip the invisible non-freeform stack tasks
+            if (i > visibleStackRange[0] && !task.isFreeformTask()) {
+                continue;
+            }
+
+            TaskView tv = mTmpTaskViewMap.get(task.key);
+            if (tv == null) {
+                tv = mViewPool.pickUpViewFromPool(task, task);
+                if (task.isFreeformTask()) {
+                    tv.updateViewPropertiesToTaskTransform(transform, TaskViewAnimation.IMMEDIATE,
+                            mRequestUpdateClippingListener);
+                } else {
+                    if (Float.compare(transform.p, 0f) <= 0) {
+                        tv.updateViewPropertiesToTaskTransform(
+                                mLayoutAlgorithm.getBackOfStackTransform(),
+                                TaskViewAnimation.IMMEDIATE, mRequestUpdateClippingListener);
+                    } else {
+                        tv.updateViewPropertiesToTaskTransform(
+                                mLayoutAlgorithm.getFrontOfStackTransform(),
+                                TaskViewAnimation.IMMEDIATE, mRequestUpdateClippingListener);
+                    }
+                }
+            } else {
+                // Reattach it in the right z order
+                final int taskIndex = mStack.indexOfStackTask(task);
+                final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
+                if (insertIndex != getTaskViews().indexOf(tv)){
+                    detachViewFromParent(tv);
+                    attachViewToParent(tv, insertIndex, tv.getLayoutParams());
+                    updateTaskViewsList();
+                }
+            }
+        }
+
+        // Update the focus if the previous focused task was returned to the view pool
+        if (lastFocusedTaskIndex != -1) {
+            if (lastFocusedTaskIndex < visibleStackRange[1]) {
+                setFocusedTask(visibleStackRange[1], false /* scrollToTask */,
+                        true /* requestViewFocus */);
+            } else {
+                setFocusedTask(visibleStackRange[0], false /* scrollToTask */,
+                        true /* requestViewFocus */);
+            }
+        }
+    }
+
+    /**
+     * Cancels any existing {@link TaskView} animations, and updates each {@link TaskView} to its
+     * current position as defined by the {@link TaskStackLayoutAlgorithm}.
+     *
+     * @param ignoreTasks the set of tasks to ignore in the relayout
+     */
+    private void updateTaskViewsToLayout(TaskViewAnimation animation, Task... ignoreTasks) {
+        // Keep track of the ignore tasks
+        ArraySet<Task> ignoreTasksSet = mTmpTaskSet;
+        ignoreTasksSet.clear();
+        ignoreTasksSet.ensureCapacity(ignoreTasks.length);
+        Collections.addAll(ignoreTasksSet, ignoreTasks);
+
+        // If we had a deferred animation, cancel that
+        mDeferredTaskViewUpdateAnimation = null;
+
+        // Cancel all task view animations
+        cancelAllTaskViewAnimations();
+
+        // Fetch the current set of TaskViews
+        bindTaskViewsWithStack(ignoreTasksSet);
+
+        // Animate them to their final transforms with the given animation
+        List<TaskView> taskViews = getTaskViews();
+        int taskViewCount = taskViews.size();
+        for (int i = 0; i < taskViewCount; i++) {
+            final TaskView tv = taskViews.get(i);
+            final int taskIndex = mStack.indexOfStackTask(tv.getTask());
+            final TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
+
+            if (ignoreTasksSet.contains(tv.getTask())) {
+                continue;
+            }
+
+            updateTaskViewToTransform(tv, transform, animation);
+        }
+    }
+
+    /**
+     * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
+     */
+    private void updateTaskViewsToLayoutOnNextFrame(TaskViewAnimation animation) {
+        mDeferredTaskViewUpdateAnimation = animation;
+        postInvalidateOnAnimation();
+    }
+
+    /**
+     * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
+     * given set of {@link TaskViewAnimation} properties.
+     */
+    public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
+            TaskViewAnimation animation) {
+        taskView.updateViewPropertiesToTaskTransform(transform, animation,
+                mRequestUpdateClippingListener);
+    }
+
+    /**
+     * Cancels all {@link TaskView} animations.
+     */
+    private void cancelAllTaskViewAnimations() {
+        List<TaskView> taskViews = getTaskViews();
+        for (int i = taskViews.size() - 1; i >= 0; i--) {
+            final TaskView tv = taskViews.get(i);
+            tv.cancelTransformAnimation();
+        }
     }
 
     /**
      * Updates the clip for each of the task views from back to front.
      */
-    void clipTaskViews(boolean forceUpdate) {
+    private void clipTaskViews() {
         RecentsConfiguration config = Recents.getConfiguration();
 
         // Update the clip on each task child
@@ -581,6 +594,7 @@
                 // Find the next view to clip against
                 for (int j = i + 1; j < taskViewCount; j++) {
                     tmpTv = taskViews.get(j);
+
                     if (tmpTv.shouldClipViewInStack()) {
                         frontTv = tmpTv;
                         break;
@@ -599,18 +613,28 @@
                     }
                 }
             }
-            tv.getViewBounds().setClipBottom(clipBottom, forceUpdate);
+            tv.getViewBounds().setClipBottom(clipBottom);
             if (!config.useHardwareLayers) {
                 tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
             }
         }
-        mStackViewsClipDirty = false;
+        mTaskViewsClipDirty = false;
     }
 
-    /** Updates the min and max virtual scroll bounds */
-    void updateLayout(boolean boundScrollToNewMinMax) {
+    /**
+     * Updates the min and max virtual scroll bounds.
+     *
+     * @param ignoreTasks the set of tasks to ignore in the relayout
+     */
+    void updateLayout(boolean boundScrollToNewMinMax, Task... ignoreTasks) {
+        // Keep track of the ingore tasks
+        ArraySet<Task> ignoreTasksSet = mTmpTaskSet;
+        ignoreTasksSet.clear();
+        ignoreTasksSet.ensureCapacity(ignoreTasks.length);
+        Collections.addAll(ignoreTasksSet, ignoreTasks);
+
         // Compute the min and max scroll values
-        mLayoutAlgorithm.update(mStack);
+        mLayoutAlgorithm.update(mStack, ignoreTasksSet);
 
         // Update the freeform workspace
         SystemServicesProxy ssp = Recents.getSystemServices();
@@ -635,8 +659,9 @@
      *
      * @return whether or not the stack will scroll as a part of this focus change
      */
-    private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated) {
-        return setFocusedTask(taskIndex, scrollToTask, animated, true);
+    private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+            final boolean requestViewFocus) {
+        return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, false);
     }
 
     /**
@@ -644,28 +669,49 @@
      *
      * @return whether or not the stack will scroll as a part of this focus change
      */
-    private boolean setFocusedTask(int taskIndex, boolean scrollToTask, final boolean animated,
-            final boolean requestViewFocus) {
+    private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+            final boolean requestViewFocus, final boolean showTimerIndicator) {
         // Find the next task to focus
-        int newFocusedTaskIndex = mStack.getStackTaskCount() > 0 ?
-                Math.max(0, Math.min(mStack.getStackTaskCount() - 1, taskIndex)) : -1;
+        int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
+                Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1;
         final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
                 mStack.getStackTasks().get(newFocusedTaskIndex) : null;
 
         // Reset the last focused task state if changed
         if (mFocusedTask != null) {
             resetFocusedTask(mFocusedTask);
+
+            // Cancel the timer indicator, if applicable
+            if (showTimerIndicator) {
+                final TaskView tv = getChildViewForTask(mFocusedTask);
+                if (tv != null) {
+                    tv.getHeaderView().cancelFocusTimerIndicator();
+                }
+            }
         }
 
         boolean willScroll = false;
+
         mFocusedTask = newFocusedTask;
+
         if (newFocusedTask != null) {
+            // Start the timer indicator, if applicable
+            if (showTimerIndicator) {
+                final TaskView tv = getChildViewForTask(mFocusedTask);
+                if (tv != null) {
+                    tv.getHeaderView().startFocusTimerIndicator();
+                } else {
+                    // The view is null; set a flag for later
+                    mStartTimerIndicator = true;
+                }
+            }
+
             Runnable focusTaskRunnable = new Runnable() {
                 @Override
                 public void run() {
-                    TaskView tv = getChildViewForTask(newFocusedTask);
+                    final TaskView tv = getChildViewForTask(newFocusedTask);
                     if (tv != null) {
-                        tv.setFocusedState(true, animated, requestViewFocus);
+                        tv.setFocusedState(true, requestViewFocus);
                     }
                 }
             };
@@ -680,10 +726,7 @@
 
                     // Cancel any running enter animations at this point when we scroll as well
                     if (!mEnterAnimationComplete) {
-                        final List<TaskView> taskViews = getTaskViews();
-                        for (TaskView tv : taskViews) {
-                            tv.cancelEnterRecentsAnimation();
-                        }
+                        cancelAllTaskViewAnimations();
                     }
                 } else {
                     focusTaskRunnable.run();
@@ -720,10 +763,28 @@
      * @param animated determines whether to actually draw the highlight along with the change in
      *                            focus.
      * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
-     *                               happens
+     *                               happens.
      */
     public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
                                        boolean cancelWindowAnimations) {
+        setRelativeFocusedTask(forward, stackTasksOnly, animated, false, false);
+    }
+
+    /**
+     * Sets the focused task relative to the currently focused task.
+     *
+     * @param forward whether to go to the next task in the stack (along the curve) or the previous
+     * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+     *                       if the currently focused task is not a stack task, will set the focus
+     *                       to the first visible stack task
+     * @param animated determines whether to actually draw the highlight along with the change in
+     *                            focus.
+     * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
+     *                               happens.
+     * @param showTimerIndicator determines whether or not to show an indicator for the task auto-advance.
+     */
+    public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
+                                       boolean cancelWindowAnimations, boolean showTimerIndicator) {
         int newIndex = mStack.indexOfStackTask(mFocusedTask);
         if (mFocusedTask != null) {
             if (stackTasksOnly) {
@@ -747,7 +808,7 @@
             } else {
                 // No restrictions, lets just move to the new task (looping forward/backwards if
                 // necessary)
-                int taskCount = mStack.getStackTaskCount();
+                int taskCount = mStack.getTaskCount();
                 newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
             }
         } else {
@@ -758,7 +819,8 @@
             }
         }
         if (newIndex != -1) {
-            boolean willScroll = setFocusedTask(newIndex, true, animated);
+            boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
+                    true /* requestViewFocus */, showTimerIndicator);
             if (willScroll && cancelWindowAnimations) {
                 // As we iterate to the next/previous task, cancel any current/lagging window
                 // transition animations
@@ -774,7 +836,7 @@
         if (task != null) {
             TaskView tv = getChildViewForTask(task);
             if (tv != null) {
-                tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
+                tv.setFocusedState(false, false /* requestViewFocus */);
             }
         }
         mFocusedTask = null;
@@ -799,7 +861,7 @@
             event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
             event.setContentDescription(frontMostTask.getTask().title);
         }
-        event.setItemCount(mStack.getStackTaskCount());
+        event.setItemCount(mStack.getTaskCount());
         event.setScrollY(mStackScroller.mScroller.getCurrY());
         event.setMaxScrollY(mStackScroller.progressToScrollRange(mLayoutAlgorithm.mMaxScrollP));
     }
@@ -815,7 +877,7 @@
             if (focusedTaskIndex > 0) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
             }
-            if (focusedTaskIndex < mStack.getStackTaskCount() - 1) {
+            if (focusedTaskIndex < mStack.getTaskCount() - 1) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
             }
         }
@@ -879,22 +941,32 @@
 
     @Override
     public void computeScroll() {
-        mStackScroller.computeScroll();
-        // Synchronize the views
-        synchronizeStackViewsWithModel();
-        clipTaskViews(false /* forceUpdate */);
-        // Notify accessibility
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+        if (mStackScroller.computeScroll()) {
+            // Notify accessibility
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+        }
+        if (mDeferredTaskViewUpdateAnimation != null) {
+            updateTaskViewsToLayout(mDeferredTaskViewUpdateAnimation);
+            mTaskViewsClipDirty = true;
+            mDeferredTaskViewUpdateAnimation = null;
+        }
+        if (mTaskViewsClipDirty) {
+            clipTaskViews();
+        }
     }
 
-    /** Computes the stack and task rects */
-    public void computeRects(Rect taskStackBounds) {
+    /**
+     * Computes the stack and task rects.
+     *
+     * @param ignoreTasks the set of tasks to ignore in the relayout
+     */
+    public void computeRects(Rect taskStackBounds, boolean boundScroll, Task... ignoreTasks) {
         // Compute the rects in the stack algorithm
         mLayoutAlgorithm.initialize(taskStackBounds,
                 TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
 
         // Update the scroll bounds
-        updateLayout(false);
+        updateLayout(boundScroll, ignoreTasks);
     }
 
     /**
@@ -914,9 +986,19 @@
         return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
     }
 
+    /**
+     * Updates the expected task stack bounds for this stack view.
+     */
     public void setTaskStackBounds(Rect taskStackBounds, Rect systemInsets) {
-        mTaskStackBounds.set(taskStackBounds);
+        // We can get spurious measure passes with the old bounds when docking, and since we are
+        // using the current stack bounds during drag and drop, don't overwrite them until we
+        // actually get new bounds
+        if (!taskStackBounds.equals(mStableStackBounds)) {
+            mStableStackBounds.set(taskStackBounds);
+            mStackBounds.set(taskStackBounds);
+        }
         mLayoutAlgorithm.setSystemInsets(systemInsets);
+        requestLayout();
     }
 
     /**
@@ -929,15 +1011,15 @@
         int height = MeasureSpec.getSize(heightMeasureSpec);
 
         // Compute our stack/task rects
-        computeRects(mTaskStackBounds);
+        computeRects(mStackBounds, false);
 
-        // If this is the first layout, then scroll to the front of the stack and synchronize the
-        // stack views immediately to load all the views
+        // If this is the first layout, then scroll to the front of the stack, then update the
+        // TaskViews with the stack so that we can lay them out
         if (mAwaitingFirstLayout) {
             mStackScroller.setStackScrollToInitialState();
-            requestSynchronizeStackViewsWithModel();
-            synchronizeStackViewsWithModel();
         }
+        mTmpTaskSet.clear();
+        bindTaskViewsWithStack(mTmpTaskSet);
 
         // Measure each of the TaskViews
         mTmpTaskViews.clear();
@@ -987,53 +1069,25 @@
                     taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
         }
 
-        if (mAwaitingFirstLayout) {
-            mAwaitingFirstLayout = false;
-            onFirstLayout();
-        }
-
-        requestSynchronizeStackViewsWithModel();
         if (changed) {
             if (mStackScroller.isScrollOutOfBounds()) {
                 mStackScroller.boundScroll();
             }
-            synchronizeStackViewsWithModel();
-            requestUpdateStackViewsClip();
-            clipTaskViews(true /* forceUpdate */);
+        }
+        updateTaskViewsToLayout(TaskViewAnimation.IMMEDIATE);
+        clipTaskViews();
+
+        if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
+            mAwaitingFirstLayout = false;
+            onFirstLayout();
+            return;
         }
     }
 
     /** Handler for the first layout. */
     void onFirstLayout() {
-        int offscreenY = mLayoutAlgorithm.mStackRect.bottom;
-
-        // Find the launch target task
-        Task launchTargetTask = mStack.getLaunchTarget();
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-
-        // Prepare the first view for its enter animation
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            TaskView tv = taskViews.get(i);
-            Task task = tv.getTask();
-            boolean hideTask = false;
-            boolean occludesLaunchTarget = false;
-            if (launchTargetTask != null) {
-                occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(task,
-                        launchTargetTask);
-                hideTask = launchTargetTask.isFreeformTask() && task.isFreeformTask();
-            }
-            tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget,
-                    offscreenY);
-        }
-
-        // If the enter animation started already and we haven't completed a layout yet, do the
-        // enter animation now
-        if (mStartEnterAnimationRequestedAfterLayout) {
-            startEnterRecentsAnimation(mStartEnterAnimationContext);
-            mStartEnterAnimationRequestedAfterLayout = false;
-            mStartEnterAnimationContext = null;
-        }
+        // Setup the view for the enter animation
+        mAnimationHelper.prepareForEnterAnimation();
 
         // Animate in the freeform workspace
         animateFreeformWorkspaceBackgroundAlpha(
@@ -1044,9 +1098,9 @@
         // until after the enter-animation
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
-        int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getStackTaskCount());
+        int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
         if (focusedTaskIndex != -1) {
-            setFocusedTask(focusedTaskIndex, false /* scrollToTask */, false /* animated */,
+            setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
                     false /* requestViewFocus */);
         }
 
@@ -1057,111 +1111,11 @@
         } else {
             EventBus.getDefault().send(new HideHistoryButtonEvent());
         }
-
-        // Start dozing
-        mUIDozeTrigger.startDozing();
     }
 
-    /** Requests this task stacks to start it's enter-recents animation */
-    public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
-        // If we are still waiting to layout, then just defer until then
-        if (mAwaitingFirstLayout) {
-            mStartEnterAnimationRequestedAfterLayout = true;
-            mStartEnterAnimationContext = ctx;
-            return;
-        }
-
-        if (mStack.getStackTaskCount() > 0) {
-            // Find the launch target task
-            Task launchTargetTask = mStack.getLaunchTarget();
-            List<TaskView> taskViews = getTaskViews();
-            int taskViewCount = taskViews.size();
-
-            // Animate all the task views into view
-            for (int i = taskViewCount - 1; i >= 0; i--) {
-                TaskView tv = taskViews.get(i);
-                Task task = tv.getTask();
-                ctx.currentTaskTransform = new TaskViewTransform();
-                ctx.currentStackViewIndex = i;
-                ctx.currentStackViewCount = taskViewCount;
-                ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
-                ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
-                        launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
-                ctx.updateListener = mRequestUpdateClippingListener;
-                mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
-                        ctx.currentTaskTransform, null);
-                tv.startEnterRecentsAnimation(ctx);
-            }
-
-            // Add a runnable to the post animation ref counter to clear all the views
-            ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
-                @Override
-                public void run() {
-                    // Poke the dozer to restart the trigger after the animation completes
-                    mUIDozeTrigger.poke();
-
-                    // Update the focused state here -- since we only set the focused task without
-                    // requesting view focus in onFirstLayout(), actually request view focus and
-                    // animate the focused state if we are alt-tabbing now, after the window enter
-                    // animation is completed
-                    if (mFocusedTask != null) {
-                        RecentsConfiguration config = Recents.getConfiguration();
-                        RecentsActivityLaunchState launchState = config.getLaunchState();
-                        setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
-                                false /* scrollToTask */, launchState.launchedWithAltTab);
-                    }
-                }
-            });
-        }
-    }
-
-    /** Requests this task stack to start it's exit-recents animation. */
-    public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
-        // Stop any scrolling
-        mStackScroller.stopScroller();
-        mStackScroller.stopBoundScrollAnimation();
-        // Animate all the task views out of view
-        ctx.offscreenTranslationY = mLayoutAlgorithm.mStackRect.bottom;
-        // Dismiss the freeform workspace background
-        int taskViewExitToHomeDuration = getResources().getInteger(
-                R.integer.recents_task_exit_to_home_duration);
-        animateFreeformWorkspaceBackgroundAlpha(0, taskViewExitToHomeDuration,
-                mFastOutSlowInInterpolator);
-
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView tv = taskViews.get(i);
-            tv.startExitToHomeAnimation(ctx);
-        }
-    }
-
-    /** Animates a task view in this stack as it launches. */
-    public void startLaunchTaskAnimation(TaskView tv, Runnable r, boolean lockToTask) {
-        Task launchTargetTask = tv.getTask();
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskView t = taskViews.get(i);
-            if (t == tv) {
-                t.setClipViewInStack(false);
-                t.startLaunchTaskAnimation(r, true, true, lockToTask);
-            } else {
-                boolean occludesLaunchTarget = launchTargetTask.group.isTaskAboveTask(t.getTask(),
-                        launchTargetTask);
-                t.startLaunchTaskAnimation(null, false, occludesLaunchTarget, lockToTask);
-            }
-        }
-    }
-
-    public boolean isTransformedTouchPointInView(float x, float y, TaskView tv) {
-        final float[] point = new float[2];
-        point[0] = x;
-        point[1] = y;
-        transformPointToViewLocal(point, tv);
-        x = point[0];
-        y = point[1];
-        return (0 <= x) && (x < tv.getWidth()) && (0 <= y) && (y < tv.getHeight());
+    public boolean isTouchPointInView(float x, float y, TaskView tv) {
+        return (tv.getLeft() <= x && x <= tv.getRight()) &&
+                (tv.getTop() <= y && y <= tv.getBottom());
     }
 
     @Override
@@ -1189,11 +1143,14 @@
      * Launches the freeform tasks.
      */
     public boolean launchFreeformTasks() {
-        Task frontTask = mStack.getStackFrontMostTask();
-        if (frontTask != null && frontTask.isFreeformTask()) {
-            EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
-                    frontTask, null, INVALID_STACK_ID, false));
-            return true;
+        ArrayList<Task> tasks = mStack.getFreeformTasks();
+        if (!tasks.isEmpty()) {
+            Task frontTask = tasks.get(tasks.size() - 1);
+            if (frontTask != null && frontTask.isFreeformTask()) {
+                EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
+                        frontTask, null, INVALID_STACK_ID, false));
+                return true;
+            }
         }
         return false;
     }
@@ -1206,7 +1163,8 @@
         updateLayout(true);
 
         // Animate all the tasks into place
-        requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
+        updateTaskViewsToLayout(new TaskViewAnimation(DEFAULT_SYNC_STACK_DURATION,
+                mFastOutSlowInInterpolator));
     }
 
     @Override
@@ -1226,11 +1184,9 @@
 
             // Get the stack scroll of the task to anchor to (since we are removing something, the
             // front most task will be our anchor task)
-            Task anchorTask = null;
+            Task anchorTask = mStack.getStackFrontMostTask();
             float prevAnchorTaskScroll = 0;
-            boolean pullStackForward = stack.getStackTaskCount() > 0;
-            if (pullStackForward) {
-                anchorTask = mStack.getStackFrontMostTask();
+            if (anchorTask != null) {
                 prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
             }
 
@@ -1241,7 +1197,7 @@
                 // Since the max scroll progress is offset from the bottom of the stack, just scroll
                 // to ensure that the new front most task is now fully visible
                 mStackScroller.setStackScroll(mLayoutAlgorithm.mMaxScrollP);
-            } else if (pullStackForward) {
+            } else if (anchorTask != null) {
                 // Otherwise, offset the scroll by the movement of the anchor task
                 float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
                 float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
@@ -1253,9 +1209,6 @@
                 mStackScroller.setStackScroll(mStackScroller.getStackScroll() + stackScrollOffset);
                 mStackScroller.boundScroll();
             }
-
-            // Animate all the tasks into place
-            requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
         } else {
             // Remove the view associated with this task, we can't rely on updateTransforms
             // to work here because the task is no longer in the list
@@ -1266,27 +1219,23 @@
 
             // Update the min/max scroll and animate other task views into their new positions
             updateLayout(true);
-
-            // Animate all the tasks into place
-            requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
         }
 
-        // Update the new front most task
-        if (newFrontMostTask != null) {
+        // Animate all the tasks into place
+        updateTaskViewsToLayout(new TaskViewAnimation(DEFAULT_SYNC_STACK_DURATION,
+                mFastOutSlowInInterpolator));
+
+        // Update the new front most task's action button
+        if (mScreenPinningEnabled && newFrontMostTask != null) {
             TaskView frontTv = getChildViewForTask(newFrontMostTask);
             if (frontTv != null) {
-                frontTv.onTaskBound(newFrontMostTask);
-                frontTv.fadeInActionButton(getResources().getInteger(
-                        R.integer.recents_task_enter_from_app_duration));
+                frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
             }
         }
 
         // If there are no remaining tasks, then just close recents
-        if (mStack.getStackTaskCount() == 0) {
-            boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0);
-            if (shouldFinishActivity) {
-                EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
-            }
+        if (mStack.getTaskCount() == 0) {
+            EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
         }
     }
 
@@ -1304,7 +1253,7 @@
 
     @Override
     public void prepareViewToEnterPool(TaskView tv) {
-        Task task = tv.getTask();
+        final Task task = tv.getTask();
 
         // Report that this tasks's data is no longer being used
         Recents.getTaskLoader().unloadTaskData(task);
@@ -1314,22 +1263,17 @@
         // Update the task views list after removing the task view
         updateTaskViewsList();
 
-        // Reset the view properties
+        // Reset the view properties and view state
         tv.resetViewProperties();
-
-        // Reset the focused view state
-        tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
-
-        // Reset the clip state of the task view
+        tv.setFocusedState(false, false /* requestViewFocus */);
         tv.setClipViewInStack(false);
+        if (mScreenPinningEnabled) {
+            tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
+        }
     }
 
     @Override
     public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
-        // It is possible for a view to be returned to the view pool before it is laid out,
-        // which means that we will need to relayout the view when it is first used next.
-        boolean requiresRelayout = tv.getWidth() <= 0 && !isNewView;
-
         // Rebind the task and request that this task's data be filled into the TaskView
         tv.onTaskBound(task);
 
@@ -1348,9 +1292,6 @@
             addView(tv, insertIndex);
         } else {
             attachViewToParent(tv, insertIndex, tv.getLayoutParams());
-            if (requiresRelayout) {
-                tv.requestLayout();
-            }
         }
         // Update the task views list after adding the new task view
         updateTaskViewsList();
@@ -1360,7 +1301,17 @@
         tv.setTouchEnabled(true);
         tv.setClipViewInStack(true);
         if (mFocusedTask == task) {
-            tv.setFocusedState(true, false /* animated */, false /* requestViewFocus */);
+            tv.setFocusedState(true, false /* requestViewFocus */);
+            if (mStartTimerIndicator) {
+                // The timer indicator couldn't be started before, so start it now
+                tv.getHeaderView().startFocusTimerIndicator();
+                mStartTimerIndicator = false;
+            }
+        }
+
+        // Restore the action button visibility if it is the front most task view
+        if (mScreenPinningEnabled && tv.getTask() == mStack.getStackFrontMostTask()) {
+            tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
         }
     }
 
@@ -1373,16 +1324,15 @@
 
     @Override
     public void onTaskViewClipStateChanged(TaskView tv) {
-        requestUpdateStackViewsClip();
+        clipTaskViews();
     }
 
     /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
 
     @Override
-    public void onScrollChanged(float prevScroll, float curScroll) {
+    public void onScrollChanged(float prevScroll, float curScroll, TaskViewAnimation animation) {
         mUIDozeTrigger.poke();
-        requestSynchronizeStackViewsWithModel();
-        postInvalidateOnAnimation();
+        updateTaskViewsToLayoutOnNextFrame(animation);
 
         if (shouldShowHistoryButton() &&
                 prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
@@ -1398,7 +1348,7 @@
 
     public final void onBusEvent(PackagesChangedEvent event) {
         // Compute which components need to be removed
-        HashSet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
+        ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
                 event.packageName, event.userId);
 
         // For other tasks, just remove them directly if they no longer exist
@@ -1409,12 +1359,7 @@
                 final TaskView tv = getChildViewForTask(t);
                 if (tv != null) {
                     // For visible children, defer removing the task until after the animation
-                    tv.startDeleteTaskAnimation(new Runnable() {
-                        @Override
-                        public void run() {
-                            removeTaskViewFromStack(tv);
-                        }
-                    }, 0);
+                    tv.dismissTask();
                 } else {
                     // Otherwise, remove the task from the stack immediately
                     mStack.removeTask(t);
@@ -1428,17 +1373,24 @@
         mUIDozeTrigger.stopDozing();
     }
 
-    public final void onBusEvent(DismissTaskViewEvent event) {
-        removeTaskViewFromStack(event.taskView);
-        EventBus.getDefault().send(new DismissTaskEvent(event.task));
+    public final void onBusEvent(LaunchTaskStartedEvent event) {
+        mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
+                event.getAnimationTrigger());
     }
 
-    public final void onBusEvent(FocusNextTaskViewEvent event) {
-        setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */);
-    }
+    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
+        // Stop any scrolling
+        mStackScroller.stopScroller();
+        mStackScroller.stopBoundScrollAnimation();
 
-    public final void onBusEvent(FocusPreviousTaskViewEvent event) {
-        setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
+        // Start the task animations
+        mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
+
+        // Dismiss the freeform workspace background
+        int taskViewExitToHomeDuration = getResources().getInteger(
+                R.integer.recents_task_exit_to_home_duration);
+        animateFreeformWorkspaceBackgroundAlpha(0, taskViewExitToHomeDuration,
+                mFastOutSlowInInterpolator);
     }
 
     public final void onBusEvent(DismissFocusedTaskViewEvent event) {
@@ -1451,9 +1403,32 @@
         }
     }
 
+    public final void onBusEvent(final DismissTaskViewEvent event) {
+        // For visible children, defer removing the task until after the animation
+        mAnimationHelper.startDeleteTaskAnimation(event.task, event.taskView,
+                event.getAnimationTrigger());
+    }
+
+    public final void onBusEvent(TaskViewDismissedEvent event) {
+        removeTaskViewFromStack(event.taskView);
+        EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
+    }
+
+    public final void onBusEvent(FocusNextTaskViewEvent event) {
+        setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
+                event.showTimerIndicator);
+    }
+
+    public final void onBusEvent(FocusPreviousTaskViewEvent event) {
+        setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
+    }
+
     public final void onBusEvent(UserInteractionEvent event) {
         // Poke the doze trigger on user interaction
         mUIDozeTrigger.poke();
+        if (event.showTimerIndicator && mFocusedTask != null) {
+            getChildViewForTask(mFocusedTask).getHeaderView().cancelFocusTimerIndicator();
+        }
     }
 
     public final void onBusEvent(RecentsVisibilityChangedEvent event) {
@@ -1468,6 +1443,15 @@
             mStackScroller.animateScroll(mStackScroller.getStackScroll(),
                     mLayoutAlgorithm.mInitialScrollP, null);
         }
+
+        // Enlarge the dragged view slightly
+        float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
+        mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
+                mTmpTransform, null);
+        mTmpTransform.scale = finalScale;
+        mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
+        updateTaskViewToTransform(event.taskView, mTmpTransform,
+                new TaskViewAnimation(DRAG_SCALE_DURATION, mFastOutSlowInInterpolator));
     }
 
     public final void onBusEvent(DragStartInitializeDropTargetsEvent event) {
@@ -1479,7 +1463,23 @@
     }
 
     public final void onBusEvent(DragDropTargetChangedEvent event) {
-        // TODO: Animate the freeform workspace background etc.
+        if (event.dropTarget instanceof TaskStack.DockState) {
+            // Calculate the new task stack bounds that matches the window size that Recents will
+            // have after the drop
+            final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+            mStackBounds.set(dockState.getDockedTaskStackBounds(getMeasuredWidth(),
+                    getMeasuredHeight(), mDividerSize, mLayoutAlgorithm.mSystemInsets,
+                    getResources()));
+            computeRects(mStackBounds, true /* boundScroll */, event.task /* ignoreTask */);
+            updateTaskViewsToLayout(new TaskViewAnimation(250, mFastOutSlowInInterpolator),
+                    event.task /* ignoreTask */);
+        } else {
+            // Restore the pre-drag task stack bounds
+            mStackBounds.set(mStableStackBounds);
+            computeRects(mStackBounds, true /* boundScroll */);
+            updateTaskViewsToLayout(new TaskViewAnimation(250, mFastOutSlowInInterpolator),
+                    event.task /* ignoreTask */);
+        }
     }
 
     public final void onBusEvent(final DragEndEvent event) {
@@ -1513,8 +1513,6 @@
                 }
             });
         }
-        event.getAnimationTrigger().increment();
-        event.taskView.animate().withEndAction(event.getAnimationTrigger().decrementAsRunnable());
 
         // We translated the view but we need to animate it back from the current layout-space rect
         // to its final layout-space rect
@@ -1528,12 +1526,19 @@
         event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
                 taskViewRect.right, taskViewRect.bottom);
 
-        // Animate the tack view back into position
-        requestSynchronizeStackViewsWithModel(250);
+        // Animate all the TaskViews back into position
+        mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
+                mTmpTransform, null);
+        event.getAnimationTrigger().increment();
+        updateTaskViewsToLayout(new TaskViewAnimation(DEFAULT_SYNC_STACK_DURATION,
+                mFastOutSlowInInterpolator));
+        updateTaskViewToTransform(event.taskView, mTmpTransform,
+                new TaskViewAnimation(DEFAULT_SYNC_STACK_DURATION, mFastOutSlowInInterpolator,
+                        event.getAnimationTrigger().decrementOnAnimationEnd()));
     }
 
     public final void onBusEvent(StackViewScrolledEvent event) {
-        mLayoutAlgorithm.updateFocusStateOnScroll(event.yMovement);
+        mLayoutAlgorithm.updateFocusStateOnScroll(event.yMovement.value);
     }
 
     public final void onBusEvent(IterateRecentsEvent event) {
@@ -1541,11 +1546,35 @@
             // Cancel the previous task's window transition before animating the focused state
             EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
         }
-        mLayoutAlgorithm.animateFocusState(mLayoutAlgorithm.getDefaultFocusState());
     }
 
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
         mEnterAnimationComplete = true;
+
+        if (mStack.getTaskCount() > 0) {
+            // Start the task enter animations
+            mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
+
+            // Add a runnable to the post animation ref counter to clear all the views
+            event.addPostAnimationCallback(new Runnable() {
+                @Override
+                public void run() {
+                    // Start the dozer to trigger to trigger any UI that shows after a timeout
+                    mUIDozeTrigger.startDozing();
+
+                    // Update the focused state here -- since we only set the focused task without
+                    // requesting view focus in onFirstLayout(), actually request view focus and
+                    // animate the focused state if we are alt-tabbing now, after the window enter
+                    // animation is completed
+                    if (mFocusedTask != null) {
+                        RecentsConfiguration config = Recents.getConfiguration();
+                        RecentsActivityLaunchState launchState = config.getLaunchState();
+                        setFocusedTask(mStack.indexOfStackTask(mFocusedTask),
+                                false /* scrollToTask */, launchState.launchedWithAltTab);
+                    }
+                }
+            });
+        }
     }
 
     public final void onBusEvent(UpdateFreeformTaskViewVisibilityEvent event) {
@@ -1561,48 +1590,11 @@
     }
 
     public final void onBusEvent(ShowHistoryEvent event) {
-        // The history view's animation will be deferred until all the stack task views are animated
-        // away
-        int historyTransitionDuration =
-                getResources().getInteger(R.integer.recents_history_transition_duration);
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            TaskView tv = taskViews.get(i);
-            tv.animate()
-                    .alpha(0f)
-                    .setDuration(historyTransitionDuration)
-                    .setUpdateListener(null)
-                    .setListener(null)
-                    .withLayer()
-                    .withEndAction(event.getAnimationTrigger().decrementAsRunnable())
-                    .start();
-            event.getAnimationTrigger().increment();
-        }
+        mAnimationHelper.startShowHistoryAnimation(event.getAnimationTrigger());
     }
 
     public final void onBusEvent(HideHistoryEvent event) {
-        // The stack task view animations will be deferred until the history view has been animated
-        // away
-        final int historyTransitionDuration =
-                getResources().getInteger(R.integer.recents_history_transition_duration);
-        List<TaskView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = taskViewCount - 1; i >= 0; i--) {
-            final TaskView tv = taskViews.get(i);
-            event.addPostAnimationCallback(new Runnable() {
-                @Override
-                public void run() {
-                    tv.animate()
-                            .alpha(1f)
-                            .setDuration(historyTransitionDuration)
-                            .setUpdateListener(null)
-                            .setListener(null)
-                            .withLayer()
-                            .start();
-                }
-            });
-        }
+        mAnimationHelper.startHideHistoryAnimation(event.getAnimationTrigger());
     }
 
     /**
@@ -1631,7 +1623,7 @@
 
         Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
         mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
-                DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
+                Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
         mFreeformWorkspaceBackgroundAnimator.setDuration(duration);
         mFreeformWorkspaceBackgroundAnimator.setInterpolator(interpolator);
         mFreeformWorkspaceBackgroundAnimator.start();
@@ -1672,4 +1664,14 @@
     private boolean shouldShowHistoryButton() {
         return !mStack.getHistoricalTasks().isEmpty();
     }
+
+    /**
+     * Reads current system flags related to accessibility and screen pinning.
+     */
+    private void readSystemFlags() {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
+        mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
+                Settings.System.LOCK_TO_APP_ENABLED) != 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 56942a8..32f02ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -34,7 +34,7 @@
     private static final boolean DEBUG = false;
 
     public interface TaskStackViewScrollerCallbacks {
-        void onScrollChanged(float prevScroll, float curScroll);
+        void onScrollChanged(float prevScroll, float curScroll, TaskViewAnimation animation);
     }
 
     Context mContext;
@@ -57,7 +57,6 @@
         mLayoutAlgorithm = layoutAlgorithm;
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.linear_out_slow_in);
-        setStackScroll(getStackScroll());
     }
 
     /** Resets the task scroller. */
@@ -75,12 +74,22 @@
         return mStackScrollP;
     }
 
-    /** Sets the current stack scroll */
+    /**
+     * Sets the current stack scroll immediately.
+     */
     public void setStackScroll(float s) {
+        setStackScroll(s, TaskViewAnimation.IMMEDIATE);
+    }
+
+    /**
+     * Sets the current stack scroll, but indicates to the callback the preferred animation to
+     * update to this new scroll.
+     */
+    public void setStackScroll(float s, TaskViewAnimation animation) {
         float prevStackScroll = mStackScrollP;
         mStackScrollP = s;
         if (mCb != null) {
-            mCb.onScrollChanged(prevStackScroll, mStackScrollP);
+            mCb.onScrollChanged(prevStackScroll, mStackScrollP, animation);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 1a6f129..a0bb0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -29,12 +30,13 @@
 import android.view.ViewParent;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
 import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -71,6 +73,7 @@
     // Used to calculate when a tap is outside a task view rectangle.
     final int mWindowTouchSlop;
 
+    private final StackViewScrolledEvent mStackViewScrolledEvent = new StackViewScrolledEvent();
     SwipeHelper mSwipeHelper;
     boolean mInterceptedBySwipeHelper;
 
@@ -79,19 +82,21 @@
         Resources res = context.getResources();
         ViewConfiguration configuration = ViewConfiguration.get(context);
         mContext = context;
+        mSv = sv;
+        mScroller = scroller;
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mScrollTouchSlop = configuration.getScaledTouchSlop();
         mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
-        mSv = sv;
-        mScroller = scroller;
         mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
-
-        float densityScale = res.getDisplayMetrics().density;
         mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_stack_overscroll);
-        mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
-                configuration.getScaledPagingTouchSlop());
-        mSwipeHelper.setMinAlpha(1f);
+        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
+            @Override
+            protected float getSize(View v) {
+                return mSv.getWidth();
+            }
+        };
+        mSwipeHelper.setDisableHardwareLayers(true);
     }
 
     /** Velocity tracker helpers */
@@ -116,7 +121,7 @@
         for (int i = taskViewCount - 1; i >= 0; i--) {
             TaskView tv = taskViews.get(i);
             if (tv.getVisibility() == View.VISIBLE) {
-                if (mSv.isTransformedTouchPointInView(x, y, tv)) {
+                if (mSv.isTouchPointInView(x, y, tv)) {
                     return tv;
                 }
             }
@@ -207,7 +212,8 @@
                     if (DEBUG) {
                         Log.d(TAG, "scroll: " + curScrollP);
                     }
-                    EventBus.getDefault().send(new StackViewScrolledEvent(y - mLastY));
+                    mStackViewScrolledEvent.updateY(y - mLastY);
+                    EventBus.getDefault().send(mStackViewScrolledEvent);
                 }
 
                 mLastY = y;
@@ -295,6 +301,7 @@
             Rect freeformRect = mSv.mLayoutAlgorithm.mFreeformRect;
             if (freeformRect.top <= y && y <= freeformRect.bottom) {
                 if (mSv.launchFreeformTasks()) {
+                    // TODO: Animate Recents away as we launch the freeform tasks
                     return;
                 }
             }
@@ -342,7 +349,7 @@
     @Override
     public void onBeginDrag(View v) {
         TaskView tv = (TaskView) v;
-        mSwipeHelper.setSnapBackTranslationX(tv.getTranslationX());
+
         // Disable clipping with the stack while we are swiping
         tv.setClipViewInStack(false);
         // Disallow touch events from this task view
@@ -355,8 +362,8 @@
     }
 
     @Override
-    public void onSwipeChanged(View v, float delta) {
-        // Do nothing
+    public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
+        return true;
     }
 
     @Override
@@ -367,14 +374,14 @@
         // Re-enable touch events from this task view
         tv.setTouchEnabled(true);
         // Remove the task view from the stack
-        EventBus.getDefault().send(new DismissTaskViewEvent(tv.getTask(), tv));
+        EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv));
         // Keep track of deletions by keyboard
         MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
                 Constants.Metrics.DismissSourceSwipeGesture);
     }
 
     @Override
-    public void onSnapBackCompleted(View v) {
+    public void onChildSnappedBack(View v) {
         TaskView tv = (TaskView) v;
         // Re-enable clipping with the stack
         tv.setClipViewInStack(true);
@@ -386,4 +393,20 @@
     public void onDragCancelled(View v) {
         // Do nothing
     }
+
+    @Override
+    public View getChildContentView(View v) {
+        return v;
+    }
+
+    @Override
+    public boolean isAntiFalsingNeeded() {
+        return false;
+    }
+
+    @Override
+    public float getFalsingThresholdFactor() {
+        return 0;
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index a3e8b2d..db4db63 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -31,7 +31,9 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewOutlineProvider;
@@ -42,35 +44,64 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.LaunchTaskEvent;
 import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
+import java.util.ArrayList;
+
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
 /* A task view */
 public class TaskView extends FrameLayout implements Task.TaskCallbacks,
-        View.OnClickListener, View.OnLongClickListener {
-
-    private final static String TAG = "TaskView";
-    private final static boolean DEBUG = false;
+        TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
 
     /** The TaskView callbacks */
     interface TaskViewCallbacks {
         void onTaskViewClipStateChanged(TaskView tv);
     }
 
+    /**
+     * The dim overlay is generally calculated from the task progress, but occasionally (like when
+     * launching) needs to be animated independently of the task progress.
+     */
+    public static final Property<TaskView, Integer> DIM =
+            new IntProperty<TaskView>("dim") {
+                @Override
+                public void setValue(TaskView tv, int dim) {
+                    tv.setDim(dim);
+                }
+
+                @Override
+                public Integer get(TaskView tv) {
+                    return tv.getDim();
+                }
+            };
+
+    public static final Property<TaskView, Float> TASK_PROGRESS =
+            new FloatProperty<TaskView>("taskProgress") {
+                @Override
+                public void setValue(TaskView tv, float p) {
+                    tv.setTaskProgress(p);
+                }
+
+                @Override
+                public Float get(TaskView tv) {
+                    return tv.getTaskProgress();
+                }
+            };
+
     float mTaskProgress;
-    ObjectAnimator mTaskProgressAnimator;
     float mMaxDimScale;
     int mDimAlpha;
     AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(3f);
@@ -80,9 +111,11 @@
 
     Task mTask;
     boolean mTaskDataLoaded;
-    boolean mClipViewInStack;
+    boolean mClipViewInStack = true;
     AnimateableViewBounds mViewBounds;
-    private AnimatorSet mClipAnimation;
+
+    private AnimatorSet mTransformAnimation;
+    private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
 
     View mContent;
     TaskViewThumbnail mThumbnailView;
@@ -93,18 +126,6 @@
     Point mDownTouchPos = new Point();
 
     Interpolator mFastOutSlowInInterpolator;
-    Interpolator mFastOutLinearInInterpolator;
-    Interpolator mQuintOutInterpolator;
-
-    // Optimizations
-    ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    setTaskProgress((Float) animation.getAnimatedValue());
-                }
-            };
-
 
     public TaskView(Context context) {
         this(context, null);
@@ -123,21 +144,15 @@
         RecentsConfiguration config = Recents.getConfiguration();
         Resources res = context.getResources();
         mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f;
-        mClipViewInStack = true;
         mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius));
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
-        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_linear_in);
-        mQuintOutInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.decelerate_quint);
-        setTaskProgress(getTaskProgress());
-        setDim(getDim());
         if (config.fakeShadows) {
             setBackground(new FakeShadowDrawable(res, config));
         }
         setOutlineProvider(mViewBounds);
+        setOnLongClickListener(this);
     }
 
     /** Set callback */
@@ -175,16 +190,25 @@
             public void getOutline(View view, Outline outline) {
                 // Set the outline to match the FAB background
                 outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
+                outline.setAlpha(0.35f);
             }
         });
+        mActionButtonView.setOnClickListener(this);
         mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
     }
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mHeaderView.onTaskViewSizeChanged(w, h);
-        mThumbnailView.onTaskViewSizeChanged(w, h);
+        if (w > 0 && h > 0) {
+            mHeaderView.onTaskViewSizeChanged(w, h);
+            mThumbnailView.onTaskViewSizeChanged(w, h);
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
     }
 
     @Override
@@ -224,300 +248,55 @@
         invalidateOutline();
     }
 
-    /** Synchronizes this view's properties with the task's transform */
-    void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int clipBottom,
-            int duration, Interpolator interpolator,
-            ValueAnimator.AnimatorUpdateListener updateCallback) {
+    void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
+            TaskViewAnimation toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
         RecentsConfiguration config = Recents.getConfiguration();
-        Utilities.cancelAnimationWithoutCallbacks(mClipAnimation);
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
 
-        // Apply the transform
-        toTransform.applyToTaskView(this, duration, interpolator, false,
-                !config.fakeShadows, updateCallback);
-
-        // Update the clipping
-        if (duration > 0) {
-            mClipAnimation = new AnimatorSet();
-            mClipAnimation.playTogether(
-                    ObjectAnimator.ofInt(mViewBounds, AnimateableViewBounds.CLIP_BOTTOM,
-                            mViewBounds.getClipBottom(), clipBottom),
-                    ObjectAnimator.ofInt(this, TaskViewTransform.LEFT, getLeft(),
-                            (int) toTransform.rect.left),
-                    ObjectAnimator.ofInt(this, TaskViewTransform.TOP, getTop(),
-                            (int) toTransform.rect.top),
-                    ObjectAnimator.ofInt(this, TaskViewTransform.RIGHT, getRight(),
-                            (int) toTransform.rect.right),
-                    ObjectAnimator.ofInt(this, TaskViewTransform.BOTTOM, getBottom(),
-                            (int) toTransform.rect.bottom),
-                    ObjectAnimator.ofFloat(mThumbnailView, TaskViewThumbnail.BITMAP_SCALE,
-                            mThumbnailView.getBitmapScale(), toTransform.thumbnailScale));
-            mClipAnimation.setStartDelay(toTransform.startDelay);
-            mClipAnimation.setDuration(duration);
-            mClipAnimation.setInterpolator(interpolator);
-            mClipAnimation.start();
+        // Compose the animations for the transform
+        mTmpAnimators.clear();
+        toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
+        if (toAnimation.isImmediate()) {
+            if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
+                setTaskProgress(toTransform.p);
+            }
+            if (toAnimation.listener != null) {
+                toAnimation.listener.onAnimationEnd(null);
+            }
         } else {
-            mViewBounds.setClipBottom(clipBottom, false /* forceUpdate */);
-            mThumbnailView.setBitmapScale(toTransform.thumbnailScale);
-            setLeftTopRightBottom((int) toTransform.rect.left, (int) toTransform.rect.top,
-                    (int) toTransform.rect.right, (int) toTransform.rect.bottom);
-        }
-        if (!config.useHardwareLayers) {
-            mThumbnailView.updateThumbnailVisibility(clipBottom - getPaddingBottom());
-        }
+            if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
+                mTmpAnimators.add(ObjectAnimator.ofFloat(this, TASK_PROGRESS, getTaskProgress(),
+                        toTransform.p));
+            }
+            ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
+            updateCallbackAnim.addUpdateListener(updateCallback);
+            mTmpAnimators.add(updateCallbackAnim);
 
-        // Update the task progress
-        Utilities.cancelAnimationWithoutCallbacks(mTaskProgressAnimator);
-        if (duration <= 0) {
-            setTaskProgress(toTransform.p);
-        } else {
-            mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
-            mTaskProgressAnimator.setDuration(duration);
-            mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
-            mTaskProgressAnimator.start();
+            // Create the animator
+            mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
+            mTransformAnimation.start();
         }
     }
 
     /** Resets this view's properties */
     void resetViewProperties() {
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
         setDim(0);
         setVisibility(View.VISIBLE);
         getViewBounds().reset();
         TaskViewTransform.reset(this);
-        if (mActionButtonView != null) {
-            mActionButtonView.setScaleX(1f);
-            mActionButtonView.setScaleY(1f);
-            mActionButtonView.setAlpha(1f);
-            mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
-        }
-    }
 
-    /** Prepares this task view for the enter-recents animations.  This is called earlier in the
-     * first layout because the actual animation into recents may take a long time. */
-    void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean hideTask,
-            boolean occludesLaunchTarget, int offscreenY) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        int initialDim = getDim();
-        if (hideTask) {
-            setVisibility(View.INVISIBLE);
-        } else if (launchState.launchedHasConfigurationChanged) {
-            // Just load the views as-is
-        } else if (launchState.launchedFromAppWithThumbnail) {
-            if (isTaskViewLaunchTargetTask) {
-                // Set the dim to 0 so we can animate it in
-                initialDim = 0;
-                // Hide the action button
-                mActionButtonView.setAlpha(0f);
-            } else if (occludesLaunchTarget) {
-                // Move the task view off screen (below) so we can animate it in
-                setTranslationY(offscreenY);
-            }
-
-        } else if (launchState.launchedFromHome) {
-            // Move the task view off screen (below) so we can animate it in
-            setTranslationY(offscreenY);
-            setTranslationZ(0);
-            setScaleX(1f);
-            setScaleY(1f);
-        }
-        // Apply the current dim
-        setDim(initialDim);
-    }
-
-    /** Animates this task view as it enters recents */
-    void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) {
-        RecentsConfiguration config = Recents.getConfiguration();
-        RecentsActivityLaunchState launchState = config.getLaunchState();
-        Resources res = mContext.getResources();
-        final TaskViewTransform transform = ctx.currentTaskTransform;
-        final int taskViewEnterFromAppDuration = res.getInteger(
-                R.integer.recents_task_enter_from_app_duration);
-        final int taskViewEnterFromHomeDuration = res.getInteger(
-                R.integer.recents_task_enter_from_home_duration);
-        final int taskViewEnterFromHomeStaggerDelay = res.getInteger(
-                R.integer.recents_task_enter_from_home_stagger_delay);
-        final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
-                R.dimen.recents_task_view_affiliate_group_enter_offset);
-
-        if (launchState.launchedFromAppWithThumbnail) {
-            if (mTask.isLaunchTarget) {
-                ctx.postAnimationTrigger.increment();
-                // Immediately start the dim animation
-                animateDimToProgress(taskViewEnterFromAppDuration,
-                        ctx.postAnimationTrigger.decrementOnAnimationEnd());
-                // Animate the action button in
-                fadeInActionButton(taskViewEnterFromAppDuration);
-            } else {
-                // Animate the task up if it was occluding the launch target
-                if (ctx.currentTaskOccludesLaunchTarget) {
-                    setTranslationY(taskViewAffiliateGroupEnterOffset);
-                    setAlpha(0f);
-                    animate().alpha(1f)
-                            .translationY(0)
-                            .setUpdateListener(null)
-                            .setListener(new AnimatorListenerAdapter() {
-                                private boolean hasEnded;
-
-                                // We use the animation listener instead of withEndAction() to
-                                // ensure that onAnimationEnd() is called when the animator is
-                                // cancelled
-                                @Override
-                                public void onAnimationEnd(Animator animation) {
-                                    if (hasEnded) return;
-                                    ctx.postAnimationTrigger.decrement();
-                                    hasEnded = true;
-                                }
-                            })
-                            .setInterpolator(mFastOutSlowInInterpolator)
-                            .setDuration(taskViewEnterFromHomeDuration)
-                            .start();
-                    ctx.postAnimationTrigger.increment();
-                }
-            }
-
-        } else if (launchState.launchedFromHome) {
-            // Animate the tasks up
-            int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
-            int delay = frontIndex * taskViewEnterFromHomeStaggerDelay;
-
-            setScaleX(transform.scale);
-            setScaleY(transform.scale);
-            if (!config.fakeShadows) {
-                animate().translationZ(transform.translationZ);
-            }
-            animate()
-                    .translationY(0)
-                    .setStartDelay(delay)
-                    .setUpdateListener(ctx.updateListener)
-                    .setListener(new AnimatorListenerAdapter() {
-                        private boolean hasEnded;
-
-                        // We use the animation listener instead of withEndAction() to ensure that
-                        // onAnimationEnd() is called when the animator is cancelled
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            if (hasEnded) return;
-                            ctx.postAnimationTrigger.decrement();
-                            hasEnded = true;
-                        }
-                    })
-                    .setInterpolator(mQuintOutInterpolator)
-                    .setDuration(taskViewEnterFromHomeDuration +
-                            frontIndex * taskViewEnterFromHomeStaggerDelay)
-                    .start();
-            ctx.postAnimationTrigger.increment();
-        }
-    }
-
-    public void cancelEnterRecentsAnimation() {
-        animate().cancel();
-    }
-
-    public void fadeInActionButton(int duration) {
-        // Hide the action button
+        mActionButtonView.setScaleX(1f);
+        mActionButtonView.setScaleY(1f);
         mActionButtonView.setAlpha(0f);
-
-        // Animate the action button in
-        mActionButtonView.animate().alpha(1f)
-                .setDuration(duration)
-                .setInterpolator(PhoneStatusBar.ALPHA_IN)
-                .start();
+        mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
     }
 
-    /** Animates this task view as it leaves recents by pressing home. */
-    void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
-        int taskViewExitToHomeDuration = getResources().getInteger(
-                R.integer.recents_task_exit_to_home_duration);
-        animate()
-                .translationY(ctx.offscreenTranslationY)
-                .setStartDelay(0)
-                .setUpdateListener(null)
-                .setListener(null)
-                .setInterpolator(mFastOutLinearInInterpolator)
-                .setDuration(taskViewExitToHomeDuration)
-                .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
-                .start();
-        ctx.postAnimationTrigger.increment();
-    }
-
-    /** Animates this task view as it exits recents */
-    void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask,
-            boolean occludesLaunchTarget, boolean lockToTask) {
-        final int taskViewExitToAppDuration = mContext.getResources().getInteger(
-                R.integer.recents_task_exit_to_app_duration);
-        final int taskViewAffiliateGroupEnterOffset = mContext.getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_affiliate_group_enter_offset);
-
-        if (isLaunchingTask) {
-            // Animate the dim
-            if (mDimAlpha > 0) {
-                ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
-                anim.setDuration(taskViewExitToAppDuration);
-                anim.setInterpolator(mFastOutLinearInInterpolator);
-                anim.start();
-            }
-
-            // Animate the action button away
-            if (!lockToTask) {
-                float toScale = 0.9f;
-                mActionButtonView.animate()
-                        .scaleX(toScale)
-                        .scaleY(toScale);
-            }
-            mActionButtonView.animate()
-                    .alpha(0f)
-                    .setStartDelay(0)
-                    .setDuration(taskViewExitToAppDuration)
-                    .setInterpolator(mFastOutLinearInInterpolator)
-                    .withEndAction(postAnimRunnable)
-                    .start();
-        } else {
-            // Hide the dismiss button
-            mHeaderView.startLaunchTaskDismissAnimation(postAnimRunnable);
-            // If this is another view in the task grouping and is in front of the launch task,
-            // animate it away first
-            if (occludesLaunchTarget) {
-                animate().alpha(0f)
-                    .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset)
-                    .setStartDelay(0)
-                    .setUpdateListener(null)
-                    .setListener(null)
-                    .setInterpolator(mFastOutLinearInInterpolator)
-                    .setDuration(taskViewExitToAppDuration)
-                    .start();
-            }
-        }
-    }
-
-    /** Animates the deletion of this task view */
-    void startDeleteTaskAnimation(final Runnable r, int delay) {
-        int taskViewRemoveAnimDuration = getResources().getInteger(
-                R.integer.recents_animate_task_view_remove_duration);
-        int taskViewRemoveAnimTranslationXPx = getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_remove_anim_translation_x);
-
-        // Disabling clipping with the stack while the view is animating away
-        setClipViewInStack(false);
-
-        animate().translationX(taskViewRemoveAnimTranslationXPx)
-            .alpha(0f)
-            .setStartDelay(delay)
-            .setUpdateListener(null)
-            .setListener(null)
-            .setInterpolator(mFastOutSlowInInterpolator)
-            .setDuration(taskViewRemoveAnimDuration)
-            .withEndAction(new Runnable() {
-                @Override
-                public void run() {
-                    if (r != null) {
-                        r.run();
-                    }
-
-                    // Re-enable clipping with the stack (we will reuse this view)
-                    setClipViewInStack(true);
-                }
-            })
-            .start();
+    /**
+     * Cancels any current transform animations.
+     */
+    public void cancelTransformAnimation() {
+        Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
     }
 
     /** Enables/disables handling touch on this task view. */
@@ -544,12 +323,14 @@
     void dismissTask() {
         // Animate out the view and call the callback
         final TaskView tv = this;
-        startDeleteTaskAnimation(new Runnable() {
+        DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv, mTask);
+        dismissEvent.addPostAnimationCallback(new Runnable() {
             @Override
             public void run() {
-                EventBus.getDefault().send(new DismissTaskViewEvent(mTask, tv));
+                EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv));
             }
-        }, 0);
+        });
+        EventBus.getDefault().send(dismissEvent);
     }
 
     /**
@@ -581,6 +362,10 @@
         updateDimFromTaskProgress();
     }
 
+    public TaskViewHeader getHeaderView() {
+        return mHeaderView;
+    }
+
     /** Returns the current task progress. */
     public float getTaskProgress() {
         return mTaskProgress;
@@ -600,12 +385,8 @@
             }
         } else {
             float dimAlpha = mDimAlpha / 255.0f;
-            if (mThumbnailView != null) {
-                mThumbnailView.setDimAlpha(dimAlpha);
-            }
-            if (mHeaderView != null) {
-                mHeaderView.setDimAlpha(dim);
-            }
+            mThumbnailView.setDimAlpha(dimAlpha);
+            mHeaderView.setDimAlpha(dimAlpha);
         }
     }
 
@@ -615,18 +396,18 @@
     }
 
     /** Animates the dim to the task progress. */
-    void animateDimToProgress(int duration, Animator.AnimatorListener postAnimRunnable) {
+    void animateDimToProgress(int duration, Animator.AnimatorListener animListener) {
         // Animate the dim into view as well
         int toDim = getDimFromTaskProgress();
         if (toDim != getDim()) {
-            ObjectAnimator anim = ObjectAnimator.ofInt(TaskView.this, "dim", toDim);
+            ObjectAnimator anim = ObjectAnimator.ofInt(this, DIM, getDim(), toDim);
             anim.setDuration(duration);
-            if (postAnimRunnable != null) {
-                anim.addListener(postAnimRunnable);
+            if (animListener != null) {
+                anim.addListener(animListener);
             }
             anim.start();
         } else {
-            postAnimRunnable.onAnimationEnd(null);
+            animListener.onAnimationEnd(null);
         }
     }
 
@@ -644,19 +425,10 @@
         setDim(getDimFromTaskProgress());
     }
 
-    /**** View focus state ****/
-
     /**
      * Explicitly sets the focused state of this task.
      */
-    public void setFocusedState(boolean isFocused, boolean animated, boolean requestViewFocus) {
-        if (DEBUG) {
-            Log.d(TAG, "setFocusedState: " + mTask.title + " focused: " + isFocused +
-                    " animated: " + animated + " requestViewFocus: " + requestViewFocus +
-                    " isFocused(): " + isFocused() +
-                    " isAccessibilityFocused(): " + isAccessibilityFocused());
-        }
-
+    public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         if (isFocused) {
             if (requestViewFocus && !isFocused()) {
@@ -672,45 +444,129 @@
         }
     }
 
-    /**** TaskCallbacks Implementation ****/
+    /**
+     * Shows the action button.
+     * @param fadeIn whether or not to animate the action button in.
+     * @param fadeInDuration the duration of the action button animation, only used if
+     *                       {@param fadeIn} is true.
+     */
+    public void showActionButton(boolean fadeIn, int fadeInDuration) {
+        mActionButtonView.setVisibility(View.VISIBLE);
 
-    /** Binds this task view to the task */
-    public void onTaskBound(Task t) {
-        mTask = t;
-        mTask.addCallback(this);
+        if (fadeIn) {
+            if (mActionButtonView.getAlpha() < 1f) {
+                mActionButtonView.animate()
+                        .alpha(1f)
+                        .scaleX(1f)
+                        .scaleY(1f)
+                        .setDuration(fadeInDuration)
+                        .setInterpolator(PhoneStatusBar.ALPHA_IN)
+                        .start();
+            }
+        } else {
+            mActionButtonView.setScaleX(1f);
+            mActionButtonView.setScaleY(1f);
+            mActionButtonView.setAlpha(1f);
+            mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
+        }
+    }
 
-        // Hide the action button if lock to app is disabled for this view
-        int lockButtonVisibility = (!t.lockToTaskEnabled || !t.lockToThisTask) ? GONE : VISIBLE;
-        if (mActionButtonView.getVisibility() != lockButtonVisibility) {
-            mActionButtonView.setVisibility(lockButtonVisibility);
-            requestLayout();
+    /**
+     * Immediately hides the action button.
+     *
+     * @param fadeOut whether or not to animate the action button out.
+     */
+    public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown,
+            final Animator.AnimatorListener animListener) {
+        if (fadeOut) {
+            if (mActionButtonView.getAlpha() > 0f) {
+                if (scaleDown) {
+                    float toScale = 0.9f;
+                    mActionButtonView.animate()
+                            .scaleX(toScale)
+                            .scaleY(toScale);
+                }
+                mActionButtonView.animate()
+                        .alpha(0f)
+                        .setDuration(fadeOutDuration)
+                        .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                        .withEndAction(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (animListener != null) {
+                                    animListener.onAnimationEnd(null);
+                                }
+                                mActionButtonView.setVisibility(View.INVISIBLE);
+                            }
+                        })
+                        .start();
+            }
+        } else {
+            mActionButtonView.setAlpha(0f);
+            mActionButtonView.setVisibility(View.INVISIBLE);
+            if (animListener != null) {
+                animListener.onAnimationEnd(null);
+            }
+        }
+    }
+
+    /**** TaskStackAnimationHelper.Callbacks Implementation ****/
+
+    @Override
+    public void onPrepareLaunchTargetForEnterAnimation() {
+        // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
+        setDim(0);
+        mActionButtonView.setAlpha(0f);
+    }
+
+    @Override
+    public void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
+            ReferenceCountedTrigger postAnimationTrigger) {
+        postAnimationTrigger.increment();
+        animateDimToProgress(duration, postAnimationTrigger.decrementOnAnimationEnd());
+
+        if (screenPinningEnabled) {
+            showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
         }
     }
 
     @Override
-    public void onTaskDataLoaded(Task task) {
-        if (mThumbnailView != null && mHeaderView != null) {
-            // Bind each of the views to the new task data
-            mThumbnailView.rebindToTask(mTask);
-            mHeaderView.rebindToTask(mTask);
-
-            // Rebind any listeners
-            mActionButtonView.setOnClickListener(this);
-            setOnLongClickListener(this);
+    public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
+            ReferenceCountedTrigger postAnimationTrigger) {
+        if (mDimAlpha > 0) {
+            ObjectAnimator anim = ObjectAnimator.ofInt(this, DIM, getDim(), 0);
+            anim.setDuration(duration);
+            anim.setInterpolator(PhoneStatusBar.ALPHA_OUT);
+            anim.start();
         }
+
+        postAnimationTrigger.increment();
+        hideActionButton(true /* fadeOut */, duration,
+                !screenPinningRequested /* scaleDown */,
+                postAnimationTrigger.decrementOnAnimationEnd());
+    }
+
+    /**** TaskCallbacks Implementation ****/
+
+    public void onTaskBound(Task t) {
+        mTask = t;
+        mTask.addCallback(this);
+    }
+
+    @Override
+    public void onTaskDataLoaded(Task task) {
+        // Bind each of the views to the new task data
+        mThumbnailView.rebindToTask(mTask);
+        mHeaderView.rebindToTask(mTask);
         mTaskDataLoaded = true;
     }
 
     @Override
     public void onTaskDataUnloaded() {
-        if (mThumbnailView != null && mHeaderView != null) {
-            // Unbind each of the views from the task data and remove the task callback
-            mTask.removeCallback(this);
-            mThumbnailView.unbindFromTask();
-            mHeaderView.unbindFromTask();
-            // Unbind any listeners
-            mActionButtonView.setOnClickListener(null);
-        }
+        // Unbind each of the views from the task data and remove the task callback
+        mTask.removeCallback(this);
+        mThumbnailView.unbindFromTask();
+        mHeaderView.unbindFromTask();
         mTaskDataLoaded = false;
     }
 
@@ -746,17 +602,6 @@
             // Start listening for drag events
             setClipViewInStack(false);
 
-            // Enlarge the view slightly
-            final float finalScale = getScaleX() * 1.05f;
-            animate()
-                    .scaleX(finalScale)
-                    .scaleY(finalScale)
-                    .setDuration(175)
-                    .setUpdateListener(null)
-                    .setListener(null)
-                    .setInterpolator(mFastOutSlowInInterpolator)
-                    .start();
-
             mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
             mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java
new file mode 100644
index 0000000..363ad66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAnimation.java
@@ -0,0 +1,78 @@
+/*
+ * 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.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.util.List;
+
+/**
+ * The animation properties to animate a {@link TaskView} to a given {@link TaskViewTransform}.
+ */
+public class TaskViewAnimation {
+
+    public static final TaskViewAnimation IMMEDIATE = new TaskViewAnimation(0,
+            new LinearInterpolator());
+
+    public final int startDelay;
+    public final int duration;
+    public final Interpolator interpolator;
+    public final Animator.AnimatorListener listener;
+
+    public TaskViewAnimation(int duration, Interpolator interpolator) {
+        this(0 /* startDelay */, duration, interpolator, null);
+    }
+
+    public TaskViewAnimation(int duration, Interpolator interpolator,
+            Animator.AnimatorListener listener) {
+        this(0 /* startDelay */, duration, interpolator, listener);
+    }
+
+    public TaskViewAnimation(int startDelay, int duration, Interpolator interpolator,
+            Animator.AnimatorListener listener) {
+        this.startDelay = startDelay;
+        this.duration = duration;
+        this.interpolator = interpolator;
+        this.listener = listener;
+    }
+
+    /**
+     * Creates a new {@link AnimatorSet} that will animate the given animators with the current
+     * animation properties.
+     */
+    public AnimatorSet createAnimator(List<Animator> animators) {
+        AnimatorSet anim = new AnimatorSet();
+        anim.setStartDelay(startDelay);
+        anim.setDuration(duration);
+        anim.setInterpolator(interpolator);
+        if (listener != null) {
+            anim.addListener(listener);
+        }
+        anim.playTogether(animators);
+        return anim;
+    }
+
+    /**
+     * Returns whether this animation has any duration.
+     */
+    public boolean isImmediate() {
+        return duration <= 0;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 9a2ffe7..e7717ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -5,7 +5,7 @@
  * 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
+ * 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,
@@ -16,26 +16,30 @@
 
 package com.android.systemui.recents.views;
 
+import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.RippleDrawable;
+import android.support.v4.graphics.ColorUtils;
+import android.os.CountDownTimer;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.ProgressBar;
 import android.widget.TextView;
 import com.android.internal.logging.MetricsLogger;
+
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
@@ -50,11 +54,71 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
-
 /* The task bar view */
 public class TaskViewHeader extends FrameLayout
         implements View.OnClickListener, View.OnLongClickListener {
 
+    private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.125f;
+    private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
+
+    /**
+     * A color drawable that draws a slight highlight at the top to help it stand out.
+     */
+    private class HighlightColorDrawable extends Drawable {
+
+        private Paint mHighlightPaint = new Paint();
+        private Paint mBackgroundPaint = new Paint();
+
+        private float[] mTmpHSL = new float[3];
+
+        public HighlightColorDrawable() {
+            mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
+            mBackgroundPaint.setAntiAlias(true);
+            mHighlightPaint.setColor(Color.argb(255, 255, 255, 255));
+            mHighlightPaint.setAntiAlias(true);
+        }
+
+        public void setColorAndDim(int color, float dimAlpha) {
+            mBackgroundPaint.setColor(color);
+
+            ColorUtils.colorToHSL(color, mTmpHSL);
+            // TODO: Consider using the saturation of the color to adjust the lightness as well
+            mTmpHSL[2] = Math.min(1f,
+                    mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
+            mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
+
+            invalidateSelf();
+        }
+
+        @Override
+        public void setColorFilter(@Nullable ColorFilter colorFilter) {
+            // Do nothing
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            // Do nothing
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            // Draw the highlight at the top edge (but put the bottom edge just out of view)
+            canvas.drawRoundRect(0, 0, mTaskViewRect.width(),
+                    2 * Math.max(mHighlightHeight, mCornerRadius),
+                    mCornerRadius, mCornerRadius, mHighlightPaint);
+
+            // Draw the background with the rounded corners
+            canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(),
+                    getHeight() + mCornerRadius,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.OPAQUE;
+        }
+    }
+
     Task mTask;
 
     // Header views
@@ -63,26 +127,35 @@
     ImageView mIconView;
     TextView mTitleView;
     int mMoveTaskTargetStackId = INVALID_STACK_ID;
+    ProgressBar mFocusTimerIndicator;
 
     // Header drawables
     Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     int mHighlightHeight;
+    float mDimAlpha;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
-    RippleDrawable mBackground;
-    GradientDrawable mBackgroundColorDrawable;
-    String mDismissContentDescription;
+    Drawable mLightFreeformIcon;
+    Drawable mDarkFreeformIcon;
+    Drawable mLightFullscreenIcon;
+    Drawable mDarkFullscreenIcon;
+    int mTaskBarViewLightTextColor;
+    int mTaskBarViewDarkTextColor;
 
-    // Static highlight that we draw at the top of each view
-    static Paint sHighlightPaint;
+    // Header background
+    private HighlightColorDrawable mBackground;
 
     // Header dim, which is only used when task view hardware layers are not used
-    Paint mDimLayerPaint = new Paint();
+    private Paint mDimLayerPaint = new Paint();
 
     Interpolator mFastOutSlowInInterpolator;
     Interpolator mFastOutLinearInInterpolator;
 
+    long mFocusIndicatorProgress;
+    private CountDownTimer mFocusTimerCountDown;
+    long mFocusTimerDuration;
+
     public TaskViewHeader(Context context) {
         this(context, null);
     }
@@ -100,29 +173,30 @@
         setWillNotDraw(false);
 
         // Load the dismiss resources
-        mDimLayerPaint.setColor(Color.argb(0, 0, 0, 0));
+        Resources res = context.getResources();
         mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
         mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
-        mDismissContentDescription =
-                context.getString(R.string.accessibility_recents_item_will_be_dismissed);
-        mCornerRadius = getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_rounded_corners_radius);
-        mHighlightHeight = getResources().getDimensionPixelSize(
-                R.dimen.recents_task_view_highlight);
+        mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
+        mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
+        mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
+        mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
+        mLightFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_light);
+        mDarkFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_dark);
+        mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
+        mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
+
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_linear_in);
 
-        // Configure the highlight paint
-        if (sHighlightPaint == null) {
-            sHighlightPaint = new Paint();
-            sHighlightPaint.setStyle(Paint.Style.STROKE);
-            sHighlightPaint.setStrokeWidth(mHighlightHeight);
-            sHighlightPaint.setColor(context.getColor(R.color.recents_task_bar_highlight_color));
-            sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
-            sHighlightPaint.setAntiAlias(true);
-        }
+        // Configure the background and dim
+        mBackground = new HighlightColorDrawable();
+        mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
+        setBackground(mBackground);
+        mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
+        mDimLayerPaint.setAntiAlias(true);
+        mFocusTimerDuration = res.getInteger(R.integer.recents_auto_advance_duration);
     }
 
     @Override
@@ -134,21 +208,12 @@
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
         mDismissButton.setOnClickListener(this);
         mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
+        mFocusTimerIndicator = (ProgressBar) findViewById(R.id.focus_timer_indicator);
 
         // Hide the backgrounds if they are ripple drawables
         if (mIconView.getBackground() instanceof RippleDrawable) {
             mIconView.setBackground(null);
         }
-
-        mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(
-                R.drawable.recents_task_view_header_bg_color);
-        // Copy the ripple drawable since we are going to be manipulating it
-        mBackground = (RippleDrawable)
-                getContext().getDrawable(R.drawable.recents_task_view_header_bg);
-        mBackground = (RippleDrawable) mBackground.mutate().getConstantState().newDrawable();
-        mBackground.setColor(ColorStateList.valueOf(0));
-        mBackground.setDrawableByLayerId(mBackground.getId(0), mBackgroundColorDrawable);
-        setBackground(mBackground);
     }
 
     /**
@@ -156,10 +221,17 @@
      * to match the frame changes.
      */
     public void onTaskViewSizeChanged(int width, int height) {
+        // Return early if the bounds have not changed
+        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
+            return;
+        }
+
         mTaskViewRect.set(0, 0, width, height);
         boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
         int appIconWidth = mIconView.getMeasuredWidth();
-        int activityDescWidth = mTitleView.getMeasuredWidth();
+        int activityDescWidth = (mTask != null)
+                ? (int) mTitleView.getPaint().measureText(mTask.title)
+                : mTitleView.getMeasuredWidth();
         int dismissIconWidth = mDismissButton.getMeasuredWidth();
         int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE
                 ? mMoveTaskButton.getMeasuredWidth()
@@ -201,31 +273,74 @@
     }
 
     @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || (who == mBackground);
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
-        // Draw the highlight at the top edge (but put the bottom edge just out of view)
-        float offset = (float) Math.ceil(mHighlightHeight / 2f);
-        float radius = mCornerRadius;
-        int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(0, 0, mTaskViewRect.width(), getMeasuredHeight());
-        canvas.drawRoundRect(-offset, 0f, (float) mTaskViewRect.width() + offset,
-                getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
-        canvas.restoreToCount(count);
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+
+        // Draw the dim layer with the rounded corners
+        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
+                mCornerRadius, mCornerRadius, mDimLayerPaint);
+    }
+
+    /** Starts the focus timer. */
+    public void startFocusTimerIndicator() {
+        mFocusTimerIndicator.setVisibility(View.VISIBLE);
+        mFocusTimerIndicator.setMax((int) mFocusTimerDuration);
+        if (mFocusTimerCountDown == null) {
+            mFocusTimerCountDown = new CountDownTimer(mFocusTimerDuration,
+                    FOCUS_INDICATOR_INTERVAL_MS) {
+                public void onTick(long millisUntilFinished) {
+                    mFocusTimerIndicator.setProgress((int) millisUntilFinished);
+                }
+
+                public void onFinish() {
+                    mFocusTimerIndicator.setProgress((int) mFocusTimerDuration);
+                }
+            }.start();
+        } else {
+            mFocusTimerCountDown.start();
+        }
+    }
+
+    /** Cancels the focus timer. */
+    public void cancelFocusTimerIndicator() {
+        if (mFocusTimerCountDown != null && mFocusTimerIndicator != null) {
+            mFocusTimerCountDown.cancel();
+            mFocusTimerIndicator.setProgress(0);
+            mFocusTimerIndicator.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    /** Returns the secondary color for a primary color. */
+    int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
+        int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
+        return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
     }
 
     /**
      * Sets the dim alpha, only used when we are not using hardware layers.
      * (see RecentsConfiguration.useHardwareLayers)
      */
-    void setDimAlpha(int alpha) {
-        mDimLayerPaint.setColor(Color.argb(alpha, 0, 0, 0));
+    void setDimAlpha(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        updateBackgroundColor(dimAlpha);
         invalidate();
     }
 
+    /**
+     * Updates the background and highlight colors for this header.
+     */
+    private void updateBackgroundColor(float dimAlpha) {
+        if (mTask != null) {
+            mBackground.setColorAndDim(mTask.colorPrimary, dimAlpha);
+            mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
+        }
+    }
+
     /** Binds the bar view to the task */
     public void rebindToTask(Task t) {
         SystemServicesProxy ssp = Recents.getSystemServices();
@@ -233,6 +348,7 @@
 
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
+        updateBackgroundColor(mDimAlpha);
         if (t.icon != null) {
             mIconView.setImageDrawable(t.icon);
         }
@@ -240,43 +356,37 @@
             mTitleView.setText(t.title);
         }
         mTitleView.setContentDescription(t.contentDescription);
-
-        // Try and apply the system ui tint
-        int existingBgColor = (getBackground() instanceof ColorDrawable) ?
-                ((ColorDrawable) getBackground()).getColor() : 0;
-        if (existingBgColor != t.colorPrimary) {
-            mBackgroundColorDrawable.setColor(t.colorPrimary);
-        }
-
-        int taskBarViewLightTextColor = getResources().getColor(
-                R.color.recents_task_bar_light_text_color);
-        int taskBarViewDarkTextColor = getResources().getColor(
-                R.color.recents_task_bar_dark_text_color);
         mTitleView.setTextColor(t.useLightOnPrimaryColor ?
-                taskBarViewLightTextColor : taskBarViewDarkTextColor);
+                mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
         mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
                 mLightDismissDrawable : mDarkDismissDrawable);
-        mDismissButton.setContentDescription(String.format(mDismissContentDescription,
-                t.contentDescription));
+        mDismissButton.setContentDescription(t.dismissDescription);
 
         // When freeform workspaces are enabled, then update the move-task button depending on the
         // current task
         if (ssp.hasFreeformWorkspaceSupport()) {
             if (t.isFreeformTask()) {
                 mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID;
-                mMoveTaskButton.setImageResource(t.useLightOnPrimaryColor
-                        ? R.drawable.recents_move_task_fullscreen_light
-                        : R.drawable.recents_move_task_fullscreen_dark);
+                mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
+                        ? mLightFullscreenIcon
+                        : mDarkFullscreenIcon);
             } else {
                 mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID;
-                mMoveTaskButton.setImageResource(t.useLightOnPrimaryColor
-                        ? R.drawable.recents_move_task_freeform_light
-                        : R.drawable.recents_move_task_freeform_dark);
+                mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
+                        ? mLightFreeformIcon
+                        : mDarkFreeformIcon);
             }
-            mMoveTaskButton.setVisibility(View.VISIBLE);
+            if (mMoveTaskButton.getVisibility() != View.VISIBLE) {
+                mMoveTaskButton.setVisibility(View.VISIBLE);
+            }
             mMoveTaskButton.setOnClickListener(this);
         }
 
+        mFocusTimerIndicator.getProgressDrawable()
+                .setColorFilter(
+                        getSecondaryColor(t.colorPrimary, t.useLightOnPrimaryColor),
+                        PorterDuff.Mode.SRC_IN);
+
         // In accessibility, a single click on the focused app info button will show it
         if (ssp.isTouchExplorationEnabled()) {
             mIconView.setOnClickListener(this);
@@ -291,22 +401,6 @@
         mMoveTaskButton.setOnClickListener(null);
     }
 
-    /** Animates this task bar dismiss button when launching a task. */
-    void startLaunchTaskDismissAnimation(final Runnable postAnimationRunanble) {
-        if (mDismissButton.getVisibility() == View.VISIBLE) {
-            int taskViewExitToAppDuration = mContext.getResources().getInteger(
-                    R.integer.recents_task_exit_to_app_duration);
-            mDismissButton.animate().cancel();
-            mDismissButton.animate()
-                    .alpha(0f)
-                    .setStartDelay(0)
-                    .setInterpolator(mFastOutSlowInInterpolator)
-                    .setDuration(taskViewExitToAppDuration)
-                    .withEndAction(postAnimationRunanble)
-                    .start();
-        }
-    }
-
     /** Animates this task bar if the user does not interact with the stack after a certain time. */
     void startNoUserInteractionAnimation() {
         if (mDismissButton.getVisibility() != View.VISIBLE) {
@@ -322,7 +416,10 @@
         }
     }
 
-    /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+    /**
+     * Mark this task view that the user does has not interacted with the stack after a certain
+     * time.
+     */
     void setNoUserInteractionState() {
         if (mDismissButton.getVisibility() != View.VISIBLE) {
             mDismissButton.animate().cancel();
@@ -331,7 +428,10 @@
         }
     }
 
-    /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
+    /**
+     * Resets the state tracking that the user has not interacted with the stack after a certain
+     * time.
+     */
     void resetNoUserInteractionState() {
         mDismissButton.setVisibility(View.INVISIBLE);
     }
@@ -345,15 +445,6 @@
     }
 
     @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-
-        // Draw the dim layer with the rounded corners
-        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight(),
-                mCornerRadius, mCornerRadius, mDimLayerPaint);
-    }
-
-    @Override
     public void onClick(View v) {
         if (v == mIconView) {
             // In accessibility, a single click on the focused app info button will show it
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 8edfae0..39d0604 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -42,26 +42,15 @@
  */
 public class TaskViewThumbnail extends View {
 
-    public static final Property<TaskViewThumbnail, Float> BITMAP_SCALE =
-            new FloatProperty<TaskViewThumbnail>("bitmapScale") {
-                @Override
-                public void setValue(TaskViewThumbnail object, float scale) {
-                    object.setBitmapScale(scale);
-                }
-
-                @Override
-                public Float get(TaskViewThumbnail object) {
-                    return object.getBitmapScale();
-                }
-            };
+    private Task mTask;
 
     // Drawing
+    Rect mThumbnailRect = new Rect();
     Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     float mDimAlpha;
     Matrix mScaleMatrix = new Matrix();
     Paint mDrawPaint = new Paint();
-    float mBitmapScale = 1f;
     BitmapShader mBitmapShader;
     LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
 
@@ -104,7 +93,13 @@
      * to match the frame changes.
      */
     public void onTaskViewSizeChanged(int width, int height) {
+        // Return early if the bounds have not changed
+        if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
+            return;
+        }
+
         mTaskViewRect.set(0, 0, width, height);
+        updateThumbnailScale();
         invalidate();
     }
 
@@ -125,9 +120,12 @@
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
                     Shader.TileMode.CLAMP);
             mDrawPaint.setShader(mBitmapShader);
+            mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
+            updateThumbnailScale();
         } else {
             mBitmapShader = null;
             mDrawPaint.setShader(null);
+            mThumbnailRect.setEmpty();
         }
         invalidate();
     }
@@ -151,12 +149,23 @@
     }
 
     /**
-     * Sets the scale of the bitmap relative to this view.
+     * Updates the scale of the bitmap relative to this view.
      */
-    public void setBitmapScale(float scale) {
+    public void updateThumbnailScale() {
         if (mBitmapShader != null) {
-            mBitmapScale = scale;
-            mScaleMatrix.setScale(mBitmapScale, mBitmapScale);
+            float thumbnailScale;
+            if (!mTask.isFreeformTask() || mTask.bounds == null) {
+                // If this is a stack task, or a stack task moved into the freeform workspace, then
+                // just scale this thumbnail to fit the width of the view
+                thumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+            } else {
+                // Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
+                // to fit the entire bitmap into the task bounds
+                thumbnailScale = Math.min(
+                        (float) mTaskViewRect.width() / mThumbnailRect.width(),
+                        (float) mTaskViewRect.height() / mThumbnailRect.height());
+            }
+            mScaleMatrix.setScale(thumbnailScale, thumbnailScale);
             mBitmapShader.setLocalMatrix(mScaleMatrix);
         }
         if (!mInvisible) {
@@ -164,10 +173,6 @@
         }
     }
 
-    public float getBitmapScale() {
-        return mBitmapScale;
-    }
-
     /** Updates the clip rect based on the given task bar. */
     void updateClipToTaskBar(View taskBar) {
         mTaskBar = taskBar;
@@ -200,6 +205,7 @@
 
     /** Binds the thumbnail view to the task */
     void rebindToTask(Task t) {
+        mTask = t;
         if (t.thumbnail != null) {
             setThumbnail(t.thumbnail);
         } else {
@@ -209,6 +215,7 @@
 
     /** Unbinds the thumbnail view from the task */
     void unbindFromTask() {
+        mTask = null;
         setThumbnail(null);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 3ee50ac..538c248 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -16,16 +16,19 @@
 
 package com.android.systemui.recents.views;
 
-import android.animation.ValueAnimator;
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.graphics.RectF;
 import android.util.IntProperty;
 import android.util.Property;
 import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.Interpolator;
 
+import java.util.ArrayList;
 
-/* The transform state for a task view */
+/**
+ * The visual properties for a {@link TaskView}.
+ */
 public class TaskViewTransform {
 
     public static final Property<View, Integer> LEFT =
@@ -80,13 +83,9 @@
                 }
             };
 
-    // TODO: Move this out of the transform
-    public int startDelay = 0;
-
     public float translationZ = 0;
     public float scale = 1f;
     public float alpha = 1f;
-    public float thumbnailScale = 1f;
 
     public boolean visible = false;
     float p = 0f;
@@ -94,19 +93,13 @@
     // This is a window-space rect used for positioning the task in the stack and freeform workspace
     public RectF rect = new RectF();
 
-    public TaskViewTransform() {
-        // Do nothing
-    }
-
     /**
      * Resets the current transform.
      */
     public void reset() {
-        startDelay = 0;
         translationZ = 0;
         scale = 1f;
         alpha = 1f;
-        thumbnailScale = 1f;
         visible = false;
         rect.setEmpty();
         p = 0f;
@@ -116,50 +109,31 @@
     public boolean hasAlphaChangedFrom(float v) {
         return (Float.compare(alpha, v) != 0);
     }
+
     public boolean hasScaleChangedFrom(float v) {
         return (Float.compare(scale, v) != 0);
     }
+
     public boolean hasTranslationZChangedFrom(float v) {
         return (Float.compare(translationZ, v) != 0);
     }
 
-    /** Applies this transform to a view. */
-    public void applyToTaskView(TaskView v, int duration, Interpolator interp, boolean allowLayers,
-            boolean allowShadows, ValueAnimator.AnimatorUpdateListener updateCallback) {
-        // Check to see if any properties have changed, and update the task view
-        if (duration > 0) {
-            ViewPropertyAnimator anim = v.animate();
-            boolean requiresLayers = false;
+    public boolean hasRectChangedFrom(View v) {
+        return ((int) rect.left != v.getLeft()) || ((int) rect.right != v.getRight()) ||
+                ((int) rect.top != v.getTop()) || ((int) rect.bottom != v.getBottom());
+    }
 
-            // Animate to the final state
-            if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
-                anim.translationZ(translationZ);
-            }
-            if (hasScaleChangedFrom(v.getScaleX())) {
-                anim.scaleX(scale)
-                    .scaleY(scale);
-                requiresLayers = true;
-            }
-            if (hasAlphaChangedFrom(v.getAlpha())) {
-                // Use layers if we animate alpha
-                anim.alpha(alpha);
-                requiresLayers = true;
-            }
-            if (requiresLayers && allowLayers) {
-                anim.withLayer();
-            }
-            if (updateCallback != null) {
-                anim.setUpdateListener(updateCallback);
-            } else {
-                anim.setUpdateListener(null);
-            }
-            anim.setListener(null);
-            anim.setStartDelay(startDelay)
-                    .setDuration(duration)
-                    .setInterpolator(interp)
-                    .start();
-        } else {
-            // Set the changed properties
+    /**
+     * Applies this transform to a view.
+     */
+    public void applyToTaskView(TaskView v, ArrayList<Animator> animators,
+            TaskViewAnimation taskAnimation, boolean allowShadows) {
+        // Return early if not visible
+        if (!visible) {
+            return;
+        }
+
+        if (taskAnimation.isImmediate()) {
             if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
                 v.setTranslationZ(translationZ);
             }
@@ -170,29 +144,42 @@
             if (hasAlphaChangedFrom(v.getAlpha())) {
                 v.setAlpha(alpha);
             }
+            if (hasRectChangedFrom(v)) {
+                v.setLeftTopRightBottom((int) rect.left, (int) rect.top, (int) rect.right,
+                        (int) rect.bottom);
+            }
+        } else {
+            if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
+                animators.add(ObjectAnimator.ofFloat(v, View.TRANSLATION_Z, v.getTranslationZ(),
+                        translationZ));
+            }
+            if (hasScaleChangedFrom(v.getScaleX())) {
+                animators.add(ObjectAnimator.ofPropertyValuesHolder(v,
+                        PropertyValuesHolder.ofFloat(View.SCALE_X, v.getScaleX(), scale),
+                        PropertyValuesHolder.ofFloat(View.SCALE_Y, v.getScaleX(), scale)));
+            }
+            if (hasAlphaChangedFrom(v.getAlpha())) {
+                animators.add(ObjectAnimator.ofFloat(v, View.ALPHA, v.getAlpha(), alpha));
+            }
+            if (hasRectChangedFrom(v)) {
+                animators.add(ObjectAnimator.ofPropertyValuesHolder(v,
+                        PropertyValuesHolder.ofInt(LEFT, v.getLeft(), (int) rect.left),
+                        PropertyValuesHolder.ofInt(TOP, v.getTop(), (int) rect.top),
+                        PropertyValuesHolder.ofInt(RIGHT, v.getRight(), (int) rect.right),
+                        PropertyValuesHolder.ofInt(BOTTOM, v.getBottom(), (int) rect.bottom)));
+            }
         }
     }
 
     /** Reset the transform on a view. */
     public static void reset(TaskView v) {
-        // Cancel any running animations and reset the translation in case something else (like a
-        // dismiss animation) changes it
-        v.animate().cancel();
         v.setTranslationX(0f);
         v.setTranslationY(0f);
         v.setTranslationZ(0f);
         v.setScaleX(1f);
         v.setScaleY(1f);
         v.setAlpha(1f);
-        v.getViewBounds().setClipBottom(0, false /* forceUpdate */);
+        v.getViewBounds().setClipBottom(0);
         v.setLeftTopRightBottom(0, 0, 0, 0);
-        v.mThumbnailView.setBitmapScale(1f);
-    }
-
-    @Override
-    public String toString() {
-        return "TaskViewTransform delay: " + startDelay + " z: " + translationZ +
-                " scale: " + scale + " alpha: " + alpha + " visible: " + visible +
-                " rect: " + rect + " p: " + p;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
deleted file mode 100644
index e1d80fd..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-
-/* Common code related to view animations */
-public class ViewAnimation {
-
-    /* The animation context for a task view animation into Recents */
-    public static class TaskViewEnterContext {
-        // A trigger to run some logic when all the animations complete.  This works around the fact
-        // that it is difficult to coordinate ViewPropertyAnimators
-        public ReferenceCountedTrigger postAnimationTrigger;
-        // An update listener to notify as the enter animation progresses (used for the home transition)
-        ValueAnimator.AnimatorUpdateListener updateListener;
-
-        // These following properties are updated for each task view we start the enter animation on
-
-        // Whether or not the current task occludes the launch target
-        boolean currentTaskOccludesLaunchTarget;
-        // The task rect for the current stack
-        Rect currentTaskRect;
-        // The transform of the current task view
-        TaskViewTransform currentTaskTransform;
-        // The view index of the current task view
-        int currentStackViewIndex;
-        // The total number of task views
-        int currentStackViewCount;
-
-        public TaskViewEnterContext(ReferenceCountedTrigger t) {
-            postAnimationTrigger = t;
-        }
-    }
-
-    /* The animation context for a task view animation out of Recents */
-    public static class TaskViewExitContext {
-        // A trigger to run some logic when all the animations complete.  This works around the fact
-        // that it is difficult to coordinate ViewPropertyAnimators
-        ReferenceCountedTrigger postAnimationTrigger;
-
-        // The translationY to apply to a TaskView to move it off the bottom of the task stack
-        int offscreenTranslationY;
-
-        public TaskViewExitContext(ReferenceCountedTrigger t) {
-            postAnimationTrigger = t;
-        }
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 6ff7a3e..189e651 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,7 +17,8 @@
 package com.android.systemui.stackdivider;
 
 import android.content.res.Configuration;
-import android.view.IDockDividerVisibilityListener;
+import android.os.RemoteException;
+import android.view.IDockedStackListener;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -49,7 +50,7 @@
         putComponent(Divider.class, this);
         mDockDividerVisibilityListener = new DockDividerVisibilityListener();
         SystemServicesProxy ssp = Recents.getSystemServices();
-        ssp.registerDockDividerVisibilityListener(mDockDividerVisibilityListener);
+        ssp.registerDockedStackListener(mDockDividerVisibilityListener);
     }
 
     @Override
@@ -94,10 +95,15 @@
         });
     }
 
-    class DockDividerVisibilityListener extends IDockDividerVisibilityListener.Stub {
+    class DockDividerVisibilityListener extends IDockedStackListener.Stub {
+
         @Override
-        public void onDockDividerVisibilityChanged(boolean visible) {
+        public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
             updateVisibility(visible);
         }
+
+        @Override
+        public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
new file mode 100644
index 0000000..5ef56f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageButton;
+
+import com.android.systemui.R;
+
+/**
+ * View for the handle in the docked stack divider.
+ */
+public class DividerHandleView extends ImageButton {
+
+    private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
+            = new Property<DividerHandleView, Integer>(Integer.class, "width") {
+
+        @Override
+        public Integer get(DividerHandleView object) {
+            return object.mCurrentWidth;
+        }
+
+        @Override
+        public void set(DividerHandleView object, Integer value) {
+            object.mCurrentWidth = value;
+            object.invalidate();
+        }
+    };
+
+    private final static Property<DividerHandleView, Integer> HEIGHT_PROPERTY
+            = new Property<DividerHandleView, Integer>(Integer.class, "height") {
+
+        @Override
+        public Integer get(DividerHandleView object) {
+            return object.mCurrentHeight;
+        }
+
+        @Override
+        public void set(DividerHandleView object, Integer value) {
+            object.mCurrentHeight = value;
+            object.invalidate();
+        }
+    };
+
+    private final Paint mPaint = new Paint();
+    private final int mWidth;
+    private final int mHeight;
+    private final int mCircleDiameter;
+    private final Interpolator mFastOutSlowInInterpolator;
+    private int mCurrentWidth;
+    private int mCurrentHeight;
+    private AnimatorSet mAnimator;
+
+    public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
+        mPaint.setAntiAlias(true);
+        mWidth = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_width);
+        mHeight = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_height);
+        mCurrentWidth = mWidth;
+        mCurrentHeight = mHeight;
+        mCircleDiameter = (mWidth + mHeight) / 3;
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+                android.R.interpolator.fast_out_slow_in);
+    }
+
+    public void setTouching(boolean touching, boolean animate) {
+        if (mAnimator != null) {
+            mAnimator.cancel();
+            mAnimator = null;
+        }
+        if (!animate) {
+            if (touching) {
+                mCurrentWidth = mCircleDiameter;
+                mCurrentHeight = mCircleDiameter;
+            } else {
+                mCurrentWidth = mWidth;
+                mCurrentHeight = mHeight;
+            }
+            invalidate();
+        } else {
+            animateToTarget(touching ? mCircleDiameter : mWidth,
+                    touching ? mCircleDiameter : mHeight, touching);
+        }
+    }
+
+    private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
+        ObjectAnimator widthAnimator = ObjectAnimator.ofInt(this, WIDTH_PROPERTY,
+                mCurrentWidth, targetWidth);
+        ObjectAnimator heightAnimator = ObjectAnimator.ofInt(this, HEIGHT_PROPERTY,
+                mCurrentHeight, targetHeight);
+        mAnimator = new AnimatorSet();
+        mAnimator.playTogether(widthAnimator, heightAnimator);
+        mAnimator.setDuration(touching
+                ? DividerView.TOUCH_ANIMATION_DURATION
+                : DividerView.TOUCH_RELEASE_ANIMATION_DURATION);
+        mAnimator.setInterpolator(touching
+                ? DividerView.TOUCH_RESPONSE_INTERPOLATOR
+                : mFastOutSlowInInterpolator);
+        mAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimator = null;
+            }
+        });
+        mAnimator.start();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int left = getWidth() / 2 - mCurrentWidth / 2;
+        int top = getHeight() / 2 - mCurrentHeight / 2;
+        int radius = Math.min(mCurrentWidth, mCurrentHeight) / 2;
+        canvas.drawRoundRect(left, top, left + mCurrentWidth, top + mCurrentHeight,
+                radius, radius, mPaint);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
deleted file mode 100644
index 5af172c..0000000
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.stackdivider;
-
-import android.content.Context;
-import android.util.DisplayMetrics;
-import android.view.DisplayInfo;
-
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.util.ArrayList;
-
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-
-/**
- * Calculates the snap targets and the snap position given a position and a velocity. All positions
- * here are to be interpreted as the left/top edge of the divider rectangle.
- */
-public class DividerSnapAlgorithm {
-
-    private final Context mContext;
-    private final FlingAnimationUtils mFlingAnimationUtils;
-    private final int mDividerSize;
-    private final ArrayList<SnapTarget> mTargets;
-
-    /** The first target which is still splitting the screen */
-    private final SnapTarget mFirstSplitTarget;
-
-    /** The last target which is still splitting the screen */
-    private final SnapTarget mLastSplitTarget;
-
-    private final SnapTarget mDismissStartTarget;
-    private final SnapTarget mDismissEndTarget;
-
-    public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils,
-            int dividerSize, boolean isHorizontalDivision) {
-        mContext = ctx;
-        mFlingAnimationUtils = flingAnimationUtils;
-        mDividerSize = dividerSize;
-        mTargets = calculateTargets(isHorizontalDivision);
-        mFirstSplitTarget = mTargets.get(1);
-        mLastSplitTarget = mTargets.get(mTargets.size() - 2);
-        mDismissStartTarget = mTargets.get(0);
-        mDismissEndTarget = mTargets.get(mTargets.size() - 1);
-    }
-
-    public SnapTarget calculateSnapTarget(int position, float velocity) {
-        if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            return snap(position);
-        }
-        if (position < mFirstSplitTarget.position && velocity < 0) {
-            return mDismissStartTarget;
-        }
-        if (position > mLastSplitTarget.position && velocity > 0) {
-            return mDismissEndTarget;
-        }
-        if (velocity < 0) {
-            return mFirstSplitTarget;
-        } else {
-            return mLastSplitTarget;
-        }
-    }
-
-    private SnapTarget snap(int position) {
-        int minIndex = -1;
-        int minDistance = Integer.MAX_VALUE;
-        int size = mTargets.size();
-        for (int i = 0; i < size; i++) {
-            int distance = Math.abs(position - mTargets.get(i).position);
-            if (distance < minDistance) {
-                minIndex = i;
-                minDistance = distance;
-            }
-        }
-        return mTargets.get(minIndex);
-    }
-
-    private ArrayList<SnapTarget> calculateTargets(boolean isHorizontalDivision) {
-        ArrayList<SnapTarget> targets = new ArrayList<>();
-        DisplayMetrics info = mContext.getResources().getDisplayMetrics();
-        int dividerMax = isHorizontalDivision
-                ? info.heightPixels
-                : info.widthPixels;
-
-        // TODO: Better calculation
-        targets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START));
-        targets.add(new SnapTarget((int) (0.3415f * dividerMax) - mDividerSize / 2,
-                SnapTarget.FLAG_NONE));
-        targets.add(new SnapTarget(dividerMax / 2 - mDividerSize / 2, SnapTarget.FLAG_NONE));
-        targets.add(new SnapTarget((int) (0.6585f * dividerMax) - mDividerSize / 2,
-                SnapTarget.FLAG_NONE));
-        targets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END));
-        return targets;
-    }
-
-    /**
-     * Represents a snap target for the divider.
-     */
-    public static class SnapTarget {
-        public static final int FLAG_NONE = 0;
-
-        /** If the divider reaches this value, the left/top task should be dismissed. */
-        public static final int FLAG_DISMISS_START = 1;
-
-        /** If the divider reaches this value, the right/bottom task should be dismissed */
-        public static final int FLAG_DISMISS_END = 2;
-
-        public final int position;
-        public final int flag;
-
-        public SnapTarget(int position, int flag) {
-            this.position = position;
-            this.flag = flag;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 13642eb..c16703e8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -21,6 +21,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
+import android.app.ActivityManager.StackId;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -34,8 +35,10 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -43,8 +46,10 @@
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
 import com.android.systemui.R;
-import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
 import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
@@ -56,15 +61,29 @@
 public class DividerView extends FrameLayout implements OnTouchListener,
         OnComputeInternalInsetsListener {
 
+    static final long TOUCH_ANIMATION_DURATION = 150;
+    static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
+    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
     private static final String TAG = "DividerView";
 
-    private ImageButton mHandle;
+    private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
+    private static final float DIM_START_FRACTION = 0.5f;
+    private static final float DIM_DAMP_FACTOR = 1.7f;
+
+    private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
+            new PathInterpolator(0.5f, 1f, 0.5f, 1f);
+
+    private DividerHandleView mHandle;
     private View mBackground;
     private int mStartX;
     private int mStartY;
     private int mStartPosition;
     private int mDockSide;
     private final int[] mTempInt2 = new int[2];
+    private boolean mMoving;
+    private int mTouchSlop;
 
     private int mDividerInsets;
     private int mDisplayWidth;
@@ -73,15 +92,20 @@
     private int mDividerSize;
     private int mTouchElevation;
 
-    private final Rect mTmpRect = new Rect();
+    private final Rect mDockedRect = new Rect();
+    private final Rect mDockedTaskRect = new Rect();
+    private final Rect mOtherTaskRect = new Rect();
+    private final Rect mOtherRect = new Rect();
+    private final Rect mDockedInsetRect = new Rect();
+    private final Rect mOtherInsetRect = new Rect();
     private final Rect mLastResizeRect = new Rect();
     private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
     private Interpolator mFastOutSlowInInterpolator;
-    private final Interpolator mTouchResponseInterpolator =
-            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
     private DividerWindowManager mWindowManager;
     private VelocityTracker mVelocityTracker;
     private FlingAnimationUtils mFlingAnimationUtils;
+    private DividerSnapAlgorithm mSnapAlgorithm;
+    private final Rect mStableInsets = new Rect();
 
     public DividerView(Context context) {
         super(context);
@@ -103,7 +127,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mHandle = (ImageButton) findViewById(R.id.docked_divider_handle);
+        mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle);
         mBackground = findViewById(R.id.docked_divider_background);
         mHandle.setOnTouchListener(this);
         mDividerWindowWidth = getResources().getDimensionPixelSize(
@@ -115,6 +139,7 @@
                 R.dimen.docked_stack_divider_lift_elevation);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.fast_out_slow_in);
+        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f);
         updateDisplayInfo();
         boolean landscape = getResources().getConfiguration().orientation
@@ -124,6 +149,13 @@
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
+                insets.getStableInsetRight(), insets.getStableInsetBottom());
+        return super.onApplyWindowInsets(insets);
+    }
+
     public void setWindowManager(DividerWindowManager windowManager) {
         mWindowManager = windowManager;
     }
@@ -132,8 +164,12 @@
         return mWindowManagerProxy;
     }
 
-    public boolean startDragging() {
+    public boolean startDragging(boolean animate) {
+        mHandle.setTouching(true, animate);
         mDockSide = mWindowManagerProxy.getDockSide();
+        mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
+                mFlingAnimationUtils.getMinVelocityPxPerSecond(), mDisplayWidth,
+                mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
         if (mDockSide != WindowManager.DOCKED_INVALID) {
             mWindowManagerProxy.setResizing(true);
             mWindowManager.setSlippery(false);
@@ -145,11 +181,16 @@
     }
 
     public void stopDragging(int position, float velocity) {
+        mHandle.setTouching(false, true /* animate */);
         fling(position, velocity);
         mWindowManager.setSlippery(true);
         releaseBackground();
     }
 
+    public DividerSnapAlgorithm getSnapAlgorithm() {
+        return mSnapAlgorithm;
+    }
+
     @Override
     public boolean onTouch(View v, MotionEvent event) {
         convertToScreenCoordinates(event);
@@ -161,19 +202,31 @@
                 mStartX = (int) event.getX();
                 mStartY = (int) event.getY();
                 getLocationOnScreen(mTempInt2);
-                boolean result = startDragging();
+                boolean result = startDragging(true /* animate */);
                 if (isHorizontalDivision()) {
                     mStartPosition = mTempInt2[1] + mDividerInsets;
                 } else {
                     mStartPosition = mTempInt2[0] + mDividerInsets;
                 }
+                mMoving = false;
                 return result;
             case MotionEvent.ACTION_MOVE:
                 mVelocityTracker.addMovement(event);
                 int x = (int) event.getX();
                 int y = (int) event.getY();
-                if (mDockSide != WindowManager.DOCKED_INVALID) {
-                    resizeStack(calculatePosition(x, y));
+                boolean exceededTouchSlop =
+                        isHorizontalDivision() && Math.abs(y - mStartY) > mTouchSlop
+                                || (!isHorizontalDivision() && Math.abs(x - mStartX) > mTouchSlop);
+                if (!mMoving && exceededTouchSlop) {
+                    mStartX = x;
+                    mStartY = y;
+                    mMoving = true;
+                }
+                if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
+                    int position = calculatePosition(x, y);
+                    SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position,
+                            0 /* velocity */);
+                    resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -187,6 +240,7 @@
                 int position = calculatePosition(x, y);
                 stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
                         : mVelocityTracker.getXVelocity());
+                mMoving = false;
                 break;
         }
         return true;
@@ -197,14 +251,16 @@
     }
 
     private void fling(int position, float velocity) {
-        final SnapTarget snapTarget = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils,
-                mDividerSize, isHorizontalDivision()).calculateSnapTarget(position, velocity);
+        final SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity);
 
         ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
-                resizeStack((Integer) animation.getAnimatedValue());
+                resizeStack((Integer) animation.getAnimatedValue(),
+                        animation.getAnimatedFraction() == 1f
+                                ? TASK_POSITION_SAME
+                                : snapTarget.position, snapTarget);
             }
         });
         anim.addListener(new AnimatorListenerAdapter() {
@@ -236,6 +292,7 @@
         } else {
             mWindowManagerProxy.maximizeDockedStack();
         }
+        mWindowManagerProxy.setResizeDimLayer(false, -1, 0f);
     }
 
     private void liftBackground() {
@@ -245,29 +302,33 @@
             mBackground.animate().scaleX(1.4f);
         }
         mBackground.animate()
-                .setInterpolator(mTouchResponseInterpolator)
-                .setDuration(150)
-                .translationZ(mTouchElevation);
+                .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
+                .setDuration(TOUCH_ANIMATION_DURATION)
+                .translationZ(mTouchElevation)
+                .start();
 
         // Lift handle as well so it doesn't get behind the background, even though it doesn't
         // cast shadow.
         mHandle.animate()
-                .setInterpolator(mTouchResponseInterpolator)
-                .setDuration(150)
-                .translationZ(mTouchElevation);
+                .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
+                .setDuration(TOUCH_ANIMATION_DURATION)
+                .translationZ(mTouchElevation)
+                .start();
     }
 
     private void releaseBackground() {
         mBackground.animate()
                 .setInterpolator(mFastOutSlowInInterpolator)
-                .setDuration(200)
+                .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
                 .translationZ(0)
                 .scaleX(1f)
-                .scaleY(1f);
+                .scaleY(1f)
+                .start();
         mHandle.animate()
                 .setInterpolator(mFastOutSlowInInterpolator)
-                .setDuration(200)
-                .translationZ(0);
+                .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
+                .translationZ(0)
+                .start();
     }
 
     @Override
@@ -302,47 +363,175 @@
         return mStartPosition + touchY - mStartY;
     }
 
-    public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
-        outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
-        switch (dockSide) {
-            case WindowManager.DOCKED_LEFT:
-                outRect.right = position;
-                break;
-            case WindowManager.DOCKED_TOP:
-                outRect.bottom = position;
-                break;
-            case WindowManager.DOCKED_RIGHT:
-                outRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
-                break;
-            case WindowManager.DOCKED_BOTTOM:
-                outRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
-                break;
-        }
-        if (outRect.left > outRect.right) {
-            outRect.left = outRect.right;
-        }
-        if (outRect.top > outRect.bottom) {
-            outRect.top = outRect.bottom;
-        }
-        if (outRect.right < outRect.left) {
-            outRect.right = outRect.left;
-        }
-        if (outRect.bottom < outRect.top) {
-            outRect.bottom = outRect.top;
-        }
+    private void alignTopLeft(Rect containingRect, Rect rect) {
+        int width = rect.width();
+        int height = rect.height();
+        rect.set(containingRect.left, containingRect.top,
+                containingRect.left + width, containingRect.top + height);
     }
 
-    public void resizeStack(int position) {
-        calculateBoundsForPosition(position, mDockSide, mTmpRect);
-        if (mTmpRect.equals(mLastResizeRect)) {
+    private void alignBottomRight(Rect containingRect, Rect rect) {
+        int width = rect.width();
+        int height = rect.height();
+        rect.set(containingRect.right - width, containingRect.bottom - height,
+                containingRect.right, containingRect.bottom);
+    }
+
+    public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
+        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
+                mDisplayHeight, mDividerSize);
+    }
+
+    public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+        calculateBoundsForPosition(position, mDockSide, mDockedRect);
+
+        if (mDockedRect.equals(mLastResizeRect)) {
             return;
         }
 
         // Make sure shadows are updated
         mBackground.invalidate();
 
-        mLastResizeRect.set(mTmpRect);
-        mWindowManagerProxy.resizeDockedStack(mTmpRect);
+        mLastResizeRect.set(mDockedRect);
+        if (taskPosition != TASK_POSITION_SAME) {
+            calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+                    mOtherRect);
+            int dockSideInverted = DockedDividerUtils.invertDockSide(mDockSide);
+            int taskPositionDocked =
+                    restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
+            int taskPositionOther =
+                    restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
+            calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
+            calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
+            alignTopLeft(mDockedRect, mDockedTaskRect);
+            alignTopLeft(mOtherRect, mOtherTaskRect);
+            mDockedInsetRect.set(mDockedTaskRect);
+            mOtherInsetRect.set(mOtherTaskRect);
+            if (dockSideTopLeft(mDockSide)) {
+                alignTopLeft(mDockedRect, mDockedInsetRect);
+                alignBottomRight(mOtherRect, mOtherInsetRect);
+            } else {
+                alignBottomRight(mDockedRect, mDockedInsetRect);
+                alignTopLeft(mOtherRect, mOtherInsetRect);
+            }
+            applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position,
+                    taskPositionDocked);
+            applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
+                    taskPositionOther);
+            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
+                    mOtherTaskRect, mOtherInsetRect);
+        } else {
+            mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
+        }
+        float fraction = mSnapAlgorithm.calculateDismissingFraction(position);
+        fraction = Math.max(0,
+                Math.min((fraction / DIM_START_FRACTION - 1f) / DIM_DAMP_FACTOR, 1f));
+        mWindowManagerProxy.setResizeDimLayer(fraction != 0f,
+                getStackIdForDismissTarget(mSnapAlgorithm.getClosestDismissTarget(position)),
+                fraction);
+    }
+
+    /**
+     * When the snap target is dismissing one side, make sure that the dismissing side doesn't get
+     * 0 size.
+     */
+    private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
+            SnapTarget snapTarget) {
+        if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
+            return mSnapAlgorithm.getFirstSplitTarget().position;
+        } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
+                && dockSideBottomRight(dockSide)) {
+            return mSnapAlgorithm.getLastSplitTarget().position;
+        } else {
+            return taskPosition;
+        }
+    }
+
+    /**
+     * Applies a parallax to the task when dismissing.
+     */
+    private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
+            int position, int taskPosition) {
+        float fraction = Math.min(1, Math.max(0,
+                mSnapAlgorithm.calculateDismissingFraction(position)));
+        SnapTarget dismissTarget = null;
+        SnapTarget splitTarget = null;
+        if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_START
+                || snapTarget == mSnapAlgorithm.getFirstSplitTarget())
+                && dockSideTopLeft(dockSide)) {
+            dismissTarget = mSnapAlgorithm.getDismissStartTarget();
+            splitTarget = mSnapAlgorithm.getFirstSplitTarget();
+        } else if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_END
+                || snapTarget == mSnapAlgorithm.getLastSplitTarget())
+                && dockSideBottomRight(dockSide)) {
+            dismissTarget = mSnapAlgorithm.getDismissEndTarget();
+            splitTarget = mSnapAlgorithm.getLastSplitTarget();
+        }
+        if (dismissTarget != null && fraction > 0f
+                && isDismissing(splitTarget, position, dockSide)) {
+            fraction = calculateParallaxDismissingFraction(fraction);
+            int offsetPosition = (int) (taskPosition +
+                    fraction * (dismissTarget.position - splitTarget.position));
+            int width = taskRect.width();
+            int height = taskRect.height();
+            switch (dockSide) {
+                case WindowManager.DOCKED_LEFT:
+                    taskRect.left = offsetPosition - width;
+                    taskRect.right = offsetPosition;
+                    break;
+                case WindowManager.DOCKED_RIGHT:
+                    taskRect.left = offsetPosition + mDividerSize;
+                    taskRect.right = offsetPosition + width + mDividerSize;
+                    break;
+                case WindowManager.DOCKED_TOP:
+                    taskRect.top = offsetPosition - height;
+                    taskRect.bottom = offsetPosition;
+                    break;
+                case WindowManager.DOCKED_BOTTOM:
+                    taskRect.top = offsetPosition + mDividerSize;
+                    taskRect.bottom = offsetPosition + height + mDividerSize;
+                    break;
+            }
+        }
+    }
+
+    /**
+     * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
+     *         slowing down parallax effect
+     */
+    private static float calculateParallaxDismissingFraction(float fraction) {
+        return SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
+    }
+
+    private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) {
+        if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) {
+            return position < snapTarget.position;
+        } else {
+            return position > snapTarget.position;
+        }
+    }
+
+    private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
+        if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START &&
+                (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) {
+            return StackId.DOCKED_STACK_ID;
+        } else {
+            return StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+        }
+    }
+
+    /**
+     * @return true if and only if {@code dockSide} is top or left
+     */
+    private static boolean dockSideTopLeft(int dockSide) {
+        return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT;
+    }
+
+    /**
+     * @return true if and only if {@code dockSide} is bottom or right
+     */
+    private static boolean dockSideBottomRight(int dockSide) {
+        return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 2251874..2294d40 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -26,6 +26,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 
 /**
@@ -50,6 +51,10 @@
                         | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         mLp.setTitle(WINDOW_TITLE);
+        mLp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+        view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         mWindowManager.addView(view, mLp);
         mView = view;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index ef47d8d..67bb58a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -40,18 +40,41 @@
     private static final WindowManagerProxy sInstance = new WindowManagerProxy();
 
     @GuardedBy("mResizeRect")
-    private final Rect mResizeRect = new Rect();
-    private final Rect mTmpRect = new Rect();
+    private final Rect mDockedRect = new Rect();
+    private final Rect mTempDockedTaskRect = new Rect();
+    private final Rect mTempDockedInsetRect = new Rect();
+    private final Rect mTempOtherTaskRect = new Rect();
+    private final Rect mTempOtherInsetRect = new Rect();
+
+    private final Rect mTmpRect1 = new Rect();
+    private final Rect mTmpRect2 = new Rect();
+    private final Rect mTmpRect3 = new Rect();
+    private final Rect mTmpRect4 = new Rect();
+    private final Rect mTmpRect5 = new Rect();
+
+    private boolean mDimLayerVisible;
+    private int mDimLayerTargetStack;
+    private float mDimLayerAlpha;
+
     private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
 
     private final Runnable mResizeRunnable = new Runnable() {
         @Override
         public void run() {
-            synchronized (mResizeRect) {
-                mTmpRect.set(mResizeRect);
+            synchronized (mDockedRect) {
+                mTmpRect1.set(mDockedRect);
+                mTmpRect2.set(mTempDockedTaskRect);
+                mTmpRect3.set(mTempDockedInsetRect);
+                mTmpRect4.set(mTempOtherTaskRect);
+                mTmpRect5.set(mTempOtherInsetRect);
             }
             try {
-                ActivityManagerNative.getDefault().resizeStack(DOCKED_STACK_ID, mTmpRect, true);
+                ActivityManagerNative.getDefault()
+                        .resizeDockedStack(mTmpRect1,
+                                mTmpRect2.isEmpty() ? null : mTmpRect2,
+                                mTmpRect3.isEmpty() ? null : mTmpRect3,
+                                mTmpRect4.isEmpty() ? null : mTmpRect4,
+                                mTmpRect5.isEmpty() ? null : mTmpRect5);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to resize stack: " + e);
             }
@@ -62,7 +85,8 @@
         @Override
         public void run() {
             try {
-                ActivityManagerNative.getDefault().moveTasksToFullscreenStack(DOCKED_STACK_ID);
+                ActivityManagerNative.getDefault().moveTasksToFullscreenStack(
+                        DOCKED_STACK_ID, false /* onTop */);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to remove stack: " + e);
             }
@@ -80,6 +104,18 @@
         }
     };
 
+    private final Runnable mDimLayerRunnable = new Runnable() {
+        @Override
+        public void run() {
+            try {
+                WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
+                        mDimLayerTargetStack, mDimLayerAlpha);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to resize stack: " + e);
+            }
+        }
+    };
+
     private WindowManagerProxy() {
     }
 
@@ -87,9 +123,30 @@
         return sInstance;
     }
 
-    public void resizeDockedStack(Rect rect) {
-        synchronized (mResizeRect) {
-            mResizeRect.set(rect);
+    public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect,
+            Rect tempOtherTaskRect, Rect tempOtherInsetRect) {
+        synchronized (mDockedRect) {
+            mDockedRect.set(docked);
+            if (tempDockedTaskRect != null) {
+                mTempDockedTaskRect.set(tempDockedTaskRect);
+            } else {
+                mTempDockedTaskRect.setEmpty();
+            }
+            if (tempDockedInsetRect != null) {
+                mTempDockedInsetRect.set(tempDockedInsetRect);
+            } else {
+                mTempDockedInsetRect.setEmpty();
+            }
+            if (tempOtherTaskRect != null) {
+                mTempOtherTaskRect.set(tempOtherTaskRect);
+            } else {
+                mTempOtherTaskRect.setEmpty();
+            }
+            if (tempOtherInsetRect != null) {
+                mTempOtherInsetRect.set(tempOtherInsetRect);
+            } else {
+                mTempOtherInsetRect.setEmpty();
+            }
         }
         mExecutor.execute(mResizeRunnable);
     }
@@ -123,4 +180,11 @@
         }
         return DOCKED_INVALID;
     }
+
+    public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+        mDimLayerVisible = visible;
+        mDimLayerTargetStack = targetStackId;
+        mDimLayerAlpha = alpha;
+        mExecutor.execute(mDimLayerRunnable);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1d7651c..ce4eff5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -16,14 +16,11 @@
 
 package com.android.systemui.statusbar;
 
-import static  android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
-import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -80,18 +77,13 @@
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AnimationUtils;
-import android.widget.DateTimeView;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
-import android.widget.SeekBar;
 import android.widget.TextView;
 import android.widget.Toast;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -116,6 +108,7 @@
 import java.util.List;
 import java.util.Locale;
 
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 
 public abstract class BaseStatusBar extends SystemUI implements
@@ -126,9 +119,6 @@
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final boolean MULTIUSER_DEBUG = false;
 
-    // STOPSHIP disable once we resolve b/18102199
-    private static final boolean NOTIFICATION_CLICK_DEBUG = true;
-
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
     public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -141,15 +131,14 @@
     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
-    protected static final int MSG_SHOW_KEYBOARD_SHORTCUTS_MENU = 1026;
+    protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
 
     protected static final boolean ENABLE_HEADS_UP = true;
-    // scores above this threshold should be displayed in heads up mode.
-    protected static final int INTERRUPTION_THRESHOLD = 10;
     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
-    // Should match the value in PhoneWindowManager
+    // Should match the values in PhoneWindowManager
     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+    public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
 
     private static final String BANNER_ACTION_CANCEL =
             "com.android.systemui.statusbar.banner_action_cancel";
@@ -199,14 +188,10 @@
     protected IDreamManager mDreamManager;
     PowerManager mPowerManager;
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    protected int mRowMinHeightLegacy;
-    protected int mRowMinHeight;
-    protected int mRowMaxHeight;
 
     // public mode, private notifications, etc
     private boolean mLockscreenPublicMode = false;
     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
-    private NotificationColorUtil mNotificationColorUtil;
 
     private UserManager mUserManager;
 
@@ -236,6 +221,8 @@
 
     private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
 
+    private KeyboardShortcuts mKeyboardShortcuts;
+
     /**
      * The {@link StatusBarState} of the status bar.
      */
@@ -356,9 +343,6 @@
                 ViewGroup actionGroup = (ViewGroup) parent;
                 index = actionGroup.indexOfChild(view);
             }
-            if (NOTIFICATION_CLICK_DEBUG) {
-                Log.d(TAG, "Clicked on button " + index + " for " + key);
-            }
             try {
                 mBarService.onNotificationActionClick(key, index);
             } catch (RemoteException e) {
@@ -616,8 +600,6 @@
         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
 
-        mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
-
         mNotificationData = new NotificationData(this);
 
         mAccessibilityManager = (AccessibilityManager)
@@ -662,13 +644,14 @@
                 android.R.interpolator.fast_out_linear_in);
 
         // Connect in to the status bar manager service
-        StatusBarIconList iconList = new StatusBarIconList();
-        mCommandQueue = new CommandQueue(this, iconList);
+        mCommandQueue = new CommandQueue(this);
 
         int[] switches = new int[8];
         ArrayList<IBinder> binders = new ArrayList<IBinder>();
+        ArrayList<String> iconSlots = new ArrayList<>();
+        ArrayList<StatusBarIcon> icons = new ArrayList<>();
         try {
-            mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
+            mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders);
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
         }
@@ -683,14 +666,10 @@
         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
 
         // Set up the initial icon state
-        int N = iconList.size();
+        int N = iconSlots.size();
         int viewIndex = 0;
-        for (int i=0; i<N; i++) {
-            StatusBarIcon icon = iconList.getIcon(i);
-            if (icon != null) {
-                addIcon(iconList.getSlot(i), i, viewIndex, icon);
-                viewIndex++;
-            }
+        for (int i=0; i < N; i++) {
+            setIcon(iconSlots.get(i), icons.get(i));
         }
 
         // Set up the initial notification state.
@@ -706,7 +685,7 @@
         if (DEBUG) {
             Log.d(TAG, String.format(
                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
-                   iconList.size(),
+                   icons.size(),
                    switches[0],
                    switches[1],
                    switches[2],
@@ -970,7 +949,7 @@
         ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
 
-        final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
+        final View settingsButton = guts.findViewById(R.id.more_settings);
         if (appUid >= 0) {
             final int appUidF = appUid;
             settingsButton.setOnClickListener(new View.OnClickListener() {
@@ -983,6 +962,14 @@
             settingsButton.setVisibility(View.GONE);
         }
 
+        row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                guts.saveImportance(sbn);
+                dismissPopups();
+            }
+        });
+
         guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
     }
 
@@ -1110,8 +1097,8 @@
     }
 
     @Override
-    public void showKeyboardShortcutsMenu() {
-        int msg = MSG_SHOW_KEYBOARD_SHORTCUTS_MENU;
+    public void toggleKeyboardShortcutsMenu() {
+        int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
         mHandler.removeMessages(msg);
         mHandler.sendEmptyMessage(msg);
     }
@@ -1134,7 +1121,7 @@
          return new H();
     }
 
-    static void sendCloseSystemWindows(Context context, String reason) {
+    protected void sendCloseSystemWindows(String reason) {
         if (ActivityManagerNative.isSystemReady()) {
             try {
                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
@@ -1169,7 +1156,7 @@
 
     protected void showRecents(boolean triggeredFromAltTab) {
         if (mRecents != null) {
-            sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
             mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
         }
     }
@@ -1182,7 +1169,6 @@
 
     protected void toggleRecents() {
         if (mRecents != null) {
-            sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
         }
     }
@@ -1193,8 +1179,8 @@
         }
     }
 
-    protected void showKeyboardShortcuts() {
-        Toast.makeText(mContext, "Show keyboard shortcuts screen", Toast.LENGTH_LONG).show();
+    protected void toggleKeyboardShortcuts() {
+        getKeyboardShortcuts().toggleKeyboardShortcuts(mContext);
     }
 
     protected void cancelPreloadingRecents() {
@@ -1317,8 +1303,8 @@
              case MSG_SHOW_PREV_AFFILIATED_TASK:
                   showRecentsPreviousAffiliatedTask();
                   break;
-             case MSG_SHOW_KEYBOARD_SHORTCUTS_MENU:
-                  showKeyboardShortcuts();
+             case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
+                  toggleKeyboardShortcuts();
                   break;
             }
         }
@@ -1524,6 +1510,14 @@
         }
     }
 
+    protected KeyboardShortcuts getKeyboardShortcuts() {
+        if (mKeyboardShortcuts == null) {
+            mKeyboardShortcuts = new KeyboardShortcuts();
+        }
+
+        return mKeyboardShortcuts;
+    }
+
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
         if (!isDeviceProvisioned()) return;
 
@@ -1606,9 +1600,6 @@
                 }
             });
 
-            if (NOTIFICATION_CLICK_DEBUG) {
-                Log.d(TAG, "Clicked on content of " + notificationKey);
-            }
             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
             final boolean afterKeyguardGone = intent.isActivity()
                     && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
@@ -1963,8 +1954,9 @@
                     + " alertAgain=" + alertAgain);
         }
 
+        final StatusBarNotification oldNotification = entry.notification;
         entry.notification = notification;
-        mGroupManager.onEntryUpdated(entry, entry.notification);
+        mGroupManager.onEntryUpdated(entry, oldNotification);
 
         boolean updateSuccessful = false;
         if (applyInPlace) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index deedae0..cc26223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,10 +21,8 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.util.Pair;
-
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarIconList;
 
 /**
  * This class takes the functions from IStatusBar that come in on
@@ -65,7 +63,7 @@
     private static final int MSG_ASSIST_DISCLOSURE          = 22 << MSG_SHIFT;
     private static final int MSG_START_ASSIST               = 23 << MSG_SHIFT;
     private static final int MSG_CAMERA_LAUNCH_GESTURE      = 24 << MSG_SHIFT;
-    private static final int MSG_SHOW_KEYBOARD_SHORTCUTS    = 25 << MSG_SHIFT;
+    private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS  = 25 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -76,7 +74,7 @@
 
     private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey";
 
-    private StatusBarIconList mList;
+    private final Object mLock = new Object();
     private Callbacks mCallbacks;
     private Handler mHandler = new H();
 
@@ -84,10 +82,8 @@
      * These methods are called back on the main thread.
      */
     public interface Callbacks {
-        public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);
-        public void updateIcon(String slot, int index, int viewIndex,
-                StatusBarIcon old, StatusBarIcon icon);
-        public void removeIcon(String slot, int index, int viewIndex);
+        public void setIcon(String slot, StatusBarIcon icon);
+        public void removeIcon(String slot);
         public void disable(int state1, int state2, boolean animate);
         public void animateExpandNotificationsPanel();
         public void animateCollapsePanels(int flags);
@@ -100,7 +96,7 @@
         public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
         public void toggleRecentApps();
         public void preloadRecentApps();
-        public void showKeyboardShortcutsMenu();
+        public void toggleKeyboardShortcutsMenu();
         public void cancelPreloadRecentApps();
         public void setWindowState(int window, int state);
         public void buzzBeepBlinked();
@@ -115,57 +111,55 @@
         public void onCameraLaunchGestureDetected(int source);
     }
 
-    public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
+    public CommandQueue(Callbacks callbacks) {
         mCallbacks = callbacks;
-        mList = list;
     }
 
-    public void setIcon(int index, StatusBarIcon icon) {
-        synchronized (mList) {
-            int what = MSG_ICON | index;
-            mHandler.removeMessages(what);
-            mHandler.obtainMessage(what, OP_SET_ICON, 0, icon.clone()).sendToTarget();
+    public void setIcon(String slot, StatusBarIcon icon) {
+        synchronized (mLock) {
+            // don't coalesce these
+            mHandler.obtainMessage(MSG_ICON, OP_SET_ICON, 0,
+                    new Pair<String, StatusBarIcon>(slot, icon)).sendToTarget();
         }
     }
 
-    public void removeIcon(int index) {
-        synchronized (mList) {
-            int what = MSG_ICON | index;
-            mHandler.removeMessages(what);
-            mHandler.obtainMessage(what, OP_REMOVE_ICON, 0, null).sendToTarget();
+    public void removeIcon(String slot) {
+        synchronized (mLock) {
+            // don't coalesce these
+            mHandler.obtainMessage(MSG_ICON, OP_REMOVE_ICON, 0, slot).sendToTarget();
         }
     }
 
     public void disable(int state1, int state2) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_DISABLE);
             mHandler.obtainMessage(MSG_DISABLE, state1, state2, null).sendToTarget();
         }
     }
 
     public void animateExpandNotificationsPanel() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_EXPAND_NOTIFICATIONS);
             mHandler.sendEmptyMessage(MSG_EXPAND_NOTIFICATIONS);
         }
     }
 
     public void animateCollapsePanels() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_COLLAPSE_PANELS);
             mHandler.sendEmptyMessage(MSG_COLLAPSE_PANELS);
         }
     }
 
     public void animateExpandSettingsPanel(String subPanel) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_EXPAND_SETTINGS);
             mHandler.obtainMessage(MSG_EXPAND_SETTINGS, subPanel).sendToTarget();
         }
     }
 
     public void setSystemUiVisibility(int vis, int mask) {
-        synchronized (mList) {
+        synchronized (mLock) {
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
             mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
@@ -173,7 +167,7 @@
     }
 
     public void topAppWindowChanged(boolean menuVisible) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
             mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
                     null).sendToTarget();
@@ -182,7 +176,7 @@
 
     public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
             Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
             m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
@@ -191,7 +185,7 @@
     }
 
     public void showRecentApps(boolean triggeredFromAltTab) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
             mHandler.obtainMessage(MSG_SHOW_RECENT_APPS,
                     triggeredFromAltTab ? 1 : 0, 0, null).sendToTarget();
@@ -199,7 +193,7 @@
     }
 
     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
             mHandler.obtainMessage(MSG_HIDE_RECENT_APPS,
                     triggeredFromAltTab ? 1 : 0, triggeredFromHomeKey ? 1 : 0,
@@ -208,83 +202,83 @@
     }
 
     public void toggleRecentApps() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_TOGGLE_RECENT_APPS);
             mHandler.obtainMessage(MSG_TOGGLE_RECENT_APPS, 0, 0, null).sendToTarget();
         }
     }
 
     public void preloadRecentApps() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_PRELOAD_RECENT_APPS);
             mHandler.obtainMessage(MSG_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
         }
     }
 
     public void cancelPreloadRecentApps() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_CANCEL_PRELOAD_RECENT_APPS);
             mHandler.obtainMessage(MSG_CANCEL_PRELOAD_RECENT_APPS, 0, 0, null).sendToTarget();
         }
     }
 
     @Override
-    public void showKeyboardShortcutsMenu() {
-        synchronized (mList) {
-            mHandler.removeMessages(MSG_SHOW_KEYBOARD_SHORTCUTS);
-            mHandler.obtainMessage(MSG_SHOW_KEYBOARD_SHORTCUTS).sendToTarget();
+    public void toggleKeyboardShortcutsMenu() {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_TOGGLE_KEYBOARD_SHORTCUTS);
+            mHandler.obtainMessage(MSG_TOGGLE_KEYBOARD_SHORTCUTS).sendToTarget();
         }
     }
 
     public void setWindowState(int window, int state) {
-        synchronized (mList) {
+        synchronized (mLock) {
             // don't coalesce these
             mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
         }
     }
 
     public void buzzBeepBlinked() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_BUZZ_BEEP_BLINKED);
             mHandler.sendEmptyMessage(MSG_BUZZ_BEEP_BLINKED);
         }
     }
 
     public void notificationLightOff() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.sendEmptyMessage(MSG_NOTIFICATION_LIGHT_OFF);
         }
     }
 
     public void notificationLightPulse(int argb, int onMillis, int offMillis) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.obtainMessage(MSG_NOTIFICATION_LIGHT_PULSE, onMillis, offMillis, argb)
                     .sendToTarget();
         }
     }
 
     public void showScreenPinningRequest() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.sendEmptyMessage(MSG_SHOW_SCREEN_PIN_REQUEST);
         }
     }
 
     public void appTransitionPending() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_PENDING);
         }
     }
 
     public void appTransitionCancelled() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_PENDING);
         }
     }
 
     public void appTransitionStarting(long startTime, long duration) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_APP_TRANSITION_STARTING);
             mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, Pair.create(startTime, duration))
                     .sendToTarget();
@@ -292,14 +286,14 @@
     }
 
     public void showAssistDisclosure() {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_ASSIST_DISCLOSURE);
             mHandler.obtainMessage(MSG_ASSIST_DISCLOSURE).sendToTarget();
         }
     }
 
     public void startAssist(Bundle args) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_START_ASSIST);
             mHandler.obtainMessage(MSG_START_ASSIST, args).sendToTarget();
         }
@@ -307,7 +301,7 @@
 
     @Override
     public void onCameraLaunchGestureDetected(int source) {
-        synchronized (mList) {
+        synchronized (mLock) {
             mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
             mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE, source, 0).sendToTarget();
         }
@@ -318,27 +312,14 @@
             final int what = msg.what & MSG_MASK;
             switch (what) {
                 case MSG_ICON: {
-                    final int index = msg.what & INDEX_MASK;
-                    final int viewIndex = mList.getViewIndex(index);
                     switch (msg.arg1) {
                         case OP_SET_ICON: {
-                            StatusBarIcon icon = (StatusBarIcon)msg.obj;
-                            StatusBarIcon old = mList.getIcon(index);
-                            if (old == null) {
-                                mList.setIcon(index, icon);
-                                mCallbacks.addIcon(mList.getSlot(index), index, viewIndex, icon);
-                            } else {
-                                mList.setIcon(index, icon);
-                                mCallbacks.updateIcon(mList.getSlot(index), index, viewIndex,
-                                        old, icon);
-                            }
+                            Pair<String, StatusBarIcon> p = (Pair<String, StatusBarIcon>) msg.obj;
+                            mCallbacks.setIcon(p.first, p.second);
                             break;
                         }
                         case OP_REMOVE_ICON:
-                            if (mList.getIcon(index) != null) {
-                                mList.removeIcon(index);
-                                mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
-                            }
+                            mCallbacks.removeIcon((String) msg.obj);
                             break;
                     }
                     break;
@@ -380,8 +361,8 @@
                 case MSG_CANCEL_PRELOAD_RECENT_APPS:
                     mCallbacks.cancelPreloadRecentApps();
                     break;
-                case MSG_SHOW_KEYBOARD_SHORTCUTS:
-                    mCallbacks.showKeyboardShortcutsMenu();
+                case MSG_TOGGLE_KEYBOARD_SHORTCUTS:
+                    mCallbacks.toggleKeyboardShortcutsMenu();
                     break;
                 case MSG_SET_WINDOW_STATE:
                     mCallbacks.setWindowState(msg.arg1, msg.arg2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
new file mode 100644
index 0000000..b36fb7e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -0,0 +1,123 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManager.KeyboardShortcutsReceiver;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+
+import java.util.List;
+
+import static android.content.Context.LAYOUT_INFLATER_SERVICE;
+import static android.graphics.Color.TRANSPARENT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+
+/**
+ * Contains functionality for handling keyboard shortcuts.
+ */
+public class KeyboardShortcuts {
+    private static final String TAG = "KeyboardShortcuts";
+
+    private Dialog mKeyboardShortcutsDialog;
+
+    public KeyboardShortcuts() {}
+
+    public void toggleKeyboardShortcuts(final Context context) {
+        if (mKeyboardShortcutsDialog == null) {
+            Recents.getSystemServices().requestKeyboardShortcuts(context,
+                new KeyboardShortcutsReceiver() {
+                    @Override
+                    public void onKeyboardShortcutsReceived(
+                            final List<KeyboardShortcutGroup> result) {
+                        KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
+                            context.getString(R.string.keyboard_shortcut_group_system));
+                        systemGroup.addItem(new KeyboardShortcutInfo(
+                            context.getString(R.string.keyboard_shortcut_group_system_home),
+                            '\u2386', KeyEvent.META_META_ON));
+                        systemGroup.addItem(new KeyboardShortcutInfo(
+                            context.getString(R.string.keyboard_shortcut_group_system_back),
+                            '\u007F', KeyEvent.META_META_ON));
+                        systemGroup.addItem(new KeyboardShortcutInfo(
+                            context.getString(R.string.keyboard_shortcut_group_system_recents),
+                            '\u0009', KeyEvent.META_ALT_ON));
+                        result.add(systemGroup);
+                        Log.i(TAG, "Keyboard shortcuts received: " + String.valueOf(result));
+                        showKeyboardShortcutsDialog(context);
+                    }
+                });
+        } else {
+            dismissKeyboardShortcutsDialog();
+        }
+    }
+
+    private void showKeyboardShortcutsDialog(Context context) {
+        // Create dialog.
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+                LAYOUT_INFLATER_SERVICE);
+        final View keyboardShortcutsView = inflater.inflate(
+                R.layout.keyboard_shortcuts_view, null);
+
+        populateKeyboardShortcuts(keyboardShortcutsView.findViewById(
+                R.id.keyboard_shortcuts_wrapper));
+        dialogBuilder.setView(keyboardShortcutsView);
+        mKeyboardShortcutsDialog = dialogBuilder.create();
+        mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
+
+        // Setup window.
+        Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
+        keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
+        keyboardShortcutsWindow.setBackgroundDrawable(
+                new ColorDrawable(TRANSPARENT));
+        keyboardShortcutsWindow.setGravity(TOP);
+        keyboardShortcutsView.post(new Runnable() {
+            public void run() {
+                mKeyboardShortcutsDialog.show();
+            }
+        });
+    }
+
+    public void dismissKeyboardShortcutsDialog() {
+        if (mKeyboardShortcutsDialog != null) {
+            mKeyboardShortcutsDialog.dismiss();
+            mKeyboardShortcutsDialog = null;
+        }
+    }
+
+    /**
+     * @return {@code true} if the keyboard shortcuts have been successfully populated.
+     */
+    private boolean populateKeyboardShortcuts(View keyboardShortcutsLayout) {
+        // TODO: Populate shortcuts.
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index f7680a7..3cc1ab9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -292,6 +292,15 @@
         return false;
     }
 
+    public boolean shouldSuppressScreenOn(String key) {
+        if (mRankingMap != null) {
+            mRankingMap.getRanking(key, mTmpRanking);
+            return (mTmpRanking.getSuppressedVisualEffects()
+                    & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0;
+        }
+        return false;
+    }
+
     public int getImportance(String key) {
         if (mRankingMap != null) {
             mRankingMap.getRanking(key, mTmpRanking);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 6850f7e..52326e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,10 +16,11 @@
 
 package com.android.systemui.statusbar;
 
-import android.annotation.IdRes;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
@@ -28,13 +29,12 @@
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.RadioButton;
-import android.widget.RadioGroup;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
 /**
@@ -46,6 +46,10 @@
     private int mClipTopAmount;
     private int mActualHeight;
     private boolean mExposed;
+    private RadioButton mApplyToTopic;
+    private SeekBar mSeekBar;
+    private Notification.Topic mTopic;
+    private INotificationManager mINotificationManager;
 
     public NotificationGuts(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -98,67 +102,56 @@
 
     void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
             final int importance) {
-        final INotificationManager sINM = INotificationManager.Stub.asInterface(
+        mINotificationManager = INotificationManager.Stub.asInterface(
                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-        final Notification.Topic topic = sbn.getNotification().getTopic() == null
+        mTopic = sbn.getNotification().getTopic() == null
                 ? new Notification.Topic(Notification.TOPIC_DEFAULT, mContext.getString(
                 com.android.internal.R.string.default_notification_topic_label))
                 : sbn.getNotification().getTopic();
         boolean doesAppUseTopics = false;
         try {
-            doesAppUseTopics = sINM.doesAppUseTopics(sbn.getPackageName(), sbn.getUid());
+            doesAppUseTopics =
+                    mINotificationManager.doesAppUseTopics(sbn.getPackageName(), sbn.getUid());
         } catch (RemoteException e) {}
         final boolean appUsesTopics = doesAppUseTopics;
 
-        final RadioButton applyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
-        applyToTopic.setChecked(true);
+        mApplyToTopic = (RadioButton) row.findViewById(R.id.apply_to_topic);
+        if (appUsesTopics) {
+            mApplyToTopic.setChecked(true);
+        }
         final View applyToApp = row.findViewById(R.id.apply_to_app);
         final TextView topicSummary = ((TextView) row.findViewById(R.id.summary));
         final TextView topicTitle = ((TextView) row.findViewById(R.id.title));
-        final SeekBar seekBar = (SeekBar) row.findViewById(R.id.seekbar);
-        final RadioGroup applyTo = (RadioGroup) row.findViewById(R.id.apply_to);
-        applyTo.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
-                try {
-                    switch (checkedId) {
-                        case R.id.apply_to_topic:
-                            sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
-                                    seekBar.getProgress());
-                            break;
-                        default:
-                            sINM.setAppImportance(sbn.getPackageName(), sbn.getUid(),
-                                    seekBar.getProgress());
-                    }
-                } catch (RemoteException e) {
-                    // :(
-                }
-            }
-        });
-
-        seekBar.setMax(4);
-        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+        mSeekBar = (SeekBar) row.findViewById(R.id.seekbar);
+        boolean systemApp = false;
+        try {
+            final PackageManager pm = BaseStatusBar.getPackageManagerForUser(
+                    getContext(), sbn.getUser().getIdentifier());
+            final PackageInfo info =
+                    pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES);
+            systemApp = Utils.isSystemPackage(pm, info);
+        } catch (PackageManager.NameNotFoundException e) {
+            // unlikely.
+        }
+        final int minProgress = systemApp ?
+                NotificationListenerService.Ranking.IMPORTANCE_LOW
+                : NotificationListenerService.Ranking.IMPORTANCE_NONE;
+        mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX);
+        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                if (progress < minProgress) {
+                    seekBar.setProgress(minProgress);
+                    progress = minProgress;
+                }
                 updateTitleAndSummary(progress);
                 if (fromUser) {
                     if (appUsesTopics) {
-                        applyToTopic.setVisibility(View.VISIBLE);
-
-                        applyToTopic.setText(
-                                mContext.getString(R.string.apply_to_topic, topic.getLabel()));
+                        mApplyToTopic.setVisibility(View.VISIBLE);
+                        mApplyToTopic.setText(
+                                mContext.getString(R.string.apply_to_topic, mTopic.getLabel()));
                         applyToApp.setVisibility(View.VISIBLE);
                     }
-                    try {
-                        if (applyToTopic.isChecked()) {
-                            sINM.setTopicImportance(sbn.getPackageName(), sbn.getUid(), topic,
-                                    progress);
-                        } else {
-                            sINM.setAppImportance(sbn.getPackageName(), sbn.getUid(), progress);
-                        }
-                    } catch (RemoteException e) {
-                        // :(
-                    }
                 }
             }
 
@@ -202,7 +195,22 @@
                 }
             }
         });
-        seekBar.setProgress(importance);
+        mSeekBar.setProgress(importance);
+    }
+
+    void saveImportance(final StatusBarNotification sbn) {
+        int progress = mSeekBar.getProgress();
+        try {
+            if (mApplyToTopic.isChecked()) {
+                mINotificationManager.setTopicImportance(sbn.getPackageName(), sbn.getUid(), mTopic,
+                        progress);
+            } else {
+                mINotificationManager.setAppImportance(
+                        sbn.getPackageName(), sbn.getUid(), progress);
+            }
+        } catch (RemoteException e) {
+            // :(
+        }
     }
 
     public void setActualHeight(int actualHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index f243b00..d7e47c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -30,11 +30,11 @@
 public class RemoteInputController {
 
     private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
-    private final StatusBarWindowManager mStatusBarWindowManager;
+    private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
     private final HeadsUpManager mHeadsUpManager;
 
     public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) {
-        mStatusBarWindowManager = sbwm;
+        addCallback(sbwm);
         mHeadsUpManager = headsUpManager;
     }
 
@@ -59,8 +59,12 @@
     }
 
     private void apply(NotificationData.Entry entry) {
-        mStatusBarWindowManager.setRemoteInputActive(isRemoteInputActive());
         mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
+        boolean remoteInputActive = isRemoteInputActive();
+        int N = mCallbacks.size();
+        for (int i = 0; i < N; i++) {
+            mCallbacks.get(i).onRemoteInputActive(remoteInputActive);
+        }
     }
 
     /**
@@ -99,4 +103,12 @@
     }
 
 
+    public void addCallback(Callback callback) {
+        Preconditions.checkNotNull(callback);
+        mCallbacks.add(callback);
+    }
+
+    public interface Callback {
+        void onRemoteInputActive(boolean active);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index db2415a..de7a8db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -30,8 +30,6 @@
 import android.util.Log;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.R;
 
@@ -76,7 +74,7 @@
             setScaleY(scale);
         }
 
-        setScaleType(ImageView.ScaleType.CENTER);
+        setScaleType(ScaleType.CENTER);
     }
 
     public void setNotification(Notification notification) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
new file mode 100644
index 0000000..3e2c4c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.support.v4.util.SimpleArrayMap;
+import android.view.View;
+import android.widget.LinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.ActivityStarter;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A controller to populate data for CarNavigationBarView and handle user interactions.
+ * <p/>
+ * Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can customize
+ * the navigation buttons by updating arrays_car.xml appropriately in an overlay.
+ */
+class CarNavigationBarController {
+
+    // Each facet of the navigation bar maps to a set of package names or categories defined in
+    // arrays_car.xml. Package names for a given facet are delimited by ";"
+    private static final String FACET_FILTER_DEMILITER = ";";
+
+    private Context mContext;
+    private CarNavigationBarView mNavBar;
+    private ActivityStarter mActivityStarter;
+
+    // Set of categories each facet will filter on.
+    private List<String[]> mFacetCategories = new ArrayList<String[]>();
+    // Set of package names each facet will filter on.
+    private List<String[]> mFacetPackages = new ArrayList<String[]>();
+
+    private SimpleArrayMap<String, Integer> mFacetCategoryMap
+            = new SimpleArrayMap<String, Integer>();
+    private SimpleArrayMap<String, Integer> mFacetPackageMap
+            = new SimpleArrayMap<String, Integer>();
+
+    private List<Intent> mIntents = new ArrayList<Intent>();
+    private List<Intent> mLongPressIntents = new ArrayList<Intent>();
+
+    private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
+
+    private int mCurrentFacetIndex;
+
+    public CarNavigationBarController(Context context,
+                                      CarNavigationBarView navBar,
+                                      ActivityStarter activityStarter) {
+        mContext = context;
+        mNavBar = navBar;
+        mActivityStarter = activityStarter;
+        bind();
+    }
+
+    public void taskChanged(String packageName) {
+        // If the package name belongs to a filter, then highlight appropriate button in
+        // the navigation bar.
+        if (mFacetPackageMap.containsKey(packageName)) {
+            setCurrentFacet(mFacetPackageMap.get(packageName));
+        }
+
+        // Check if the package matches any of the categories for the facets
+        String category = getPackageCategory(packageName);
+        if (category != null) {
+            setCurrentFacet(mFacetCategoryMap.get(category));
+        }
+    }
+
+    private void bind() {
+        // Read up arrays_car.xml and populate the navigation bar here.
+        Resources r = mContext.getResources();
+        TypedArray icons = r.obtainTypedArray(R.array.car_facet_icons);
+        TypedArray intents = r.obtainTypedArray(R.array.car_facet_intent_uris);
+        TypedArray longpressIntents =
+                r.obtainTypedArray(R.array.car_facet_longpress_intent_uris);
+        TypedArray facetPackageNames = r.obtainTypedArray(R.array.car_facet_package_filters);
+
+        TypedArray facetCategories = r.obtainTypedArray(R.array.car_facet_category_filters);
+
+        if (icons.length() != intents.length()
+                || icons.length() != longpressIntents.length()
+                || icons.length() != facetPackageNames.length()
+                || icons.length() != facetCategories.length()) {
+            throw new RuntimeException("car_facet array lengths do not match");
+        }
+
+        for (int i = 0; i < icons.length(); i++) {
+            Drawable icon = icons.getDrawable(i);
+            try {
+                mIntents.add(i,
+                        Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME));
+
+                String longpressUri = longpressIntents.getString(i);
+                boolean hasLongpress = !longpressUri.isEmpty();
+                if (hasLongpress) {
+                    mLongPressIntents.add(i,
+                            Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME));
+                }
+
+                CarNavigationButton button = createNavButton(icon, i, hasLongpress);
+                mNavButtons.add(button);
+                mNavBar.addButton(button, createNavButton(icon, i, hasLongpress));
+
+                initFacetFilterMaps(i,
+                        facetPackageNames.getString(i).split(FACET_FILTER_DEMILITER),
+                        facetCategories.getString(i).split(FACET_FILTER_DEMILITER));
+            } catch (URISyntaxException e) {
+                throw new RuntimeException("Malformed intent uri", e);
+            }
+        }
+    }
+
+    private void initFacetFilterMaps(int id, String[] packageNames, String[] categories){
+        mFacetCategories.add(categories);
+        for (int i = 0; i < categories.length; i++) {
+            mFacetCategoryMap.put(categories[i], id);
+        }
+
+        mFacetPackages.add(packageNames);
+        for (int i = 0; i < packageNames.length; i++) {
+            mFacetPackageMap.put(packageNames[i], id);
+        }
+    }
+
+    private String getPackageCategory(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        int size = mFacetCategories.size();
+        // For each facet, check if the given package name matches one of its categories
+        for (int i = 0; i < size; i++) {
+            String[] categories = mFacetCategories.get(i);
+            for (int j = 0; j < categories.length; j++) {
+                String category = categories[j];
+                Intent intent = new Intent();
+                intent.setPackage(packageName);
+                intent.setAction(Intent.ACTION_MAIN);
+                intent.addCategory(category);
+                List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+                if (list.size() > 0) {
+                    // Cache this package name into facetPackageMap, so we won't have to query
+                    // all categories next time this package name shows up.
+                    mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category));
+                    return category;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Helper method to check if a given facet has multiple packages associated with it.
+     * This can be resource defined package names or package names filtered by facet category.
+     */
+    private boolean facetHasMultiplePackages(int index) {
+        PackageManager pm = mContext.getPackageManager();
+
+        // Check if the packages defined for the filter actually exists on the device
+        String[] packages = mFacetPackages.get(index);
+        if (packages.length > 1) {
+            int count = 0;
+            for (int i = 0; i < packages.length; i++) {
+                count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0;
+                if (count > 1) {
+                    return true;
+                }
+            }
+        }
+
+        // If there weren't multiple packages defined for the facet, check the categories
+        // and see if they resolve to multiple package names
+        String categories[] = mFacetCategories.get(index);
+
+        int count = 0;
+        for (int i = 0; i < categories.length; i++) {
+            String category = categories[i];
+            Intent intent = new Intent();
+            intent.setAction(Intent.ACTION_MAIN);
+            intent.addCategory(category);
+            count += pm.queryIntentActivities(intent, 0).size();
+            if (count > 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void setCurrentFacet(int index) {
+        if (index == mCurrentFacetIndex) {
+            return;
+        }
+
+        if (mNavButtons.get(mCurrentFacetIndex) != null) {
+            mNavButtons.get(mCurrentFacetIndex)
+                    .setSelected(false /* selected */, false /* showMoreIcon */);
+        }
+
+        if (mNavButtons.get(index) != null) {
+            mNavButtons.get(index).setSelected(true /* selected */,
+                    facetHasMultiplePackages(index)  /* showMoreIcon */);
+        }
+        mCurrentFacetIndex = index;
+    }
+
+    private CarNavigationButton createNavButton(Drawable icon, final int id,
+                                                boolean longClickEnabled) {
+        CarNavigationButton button = (CarNavigationButton) View.inflate(mContext,
+                R.layout.car_navigation_button, null);
+        button.setResources(icon);
+        LinearLayout.LayoutParams lp =
+                new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
+        button.setLayoutParams(lp);
+
+        button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setCurrentFacet(id);
+                onFacetClicked(id);
+            }
+        });
+
+        if (longClickEnabled) {
+            button.setLongClickable(true);
+            button.setOnLongClickListener(new View.OnLongClickListener() {
+                @Override
+                public boolean onLongClick(View v) {
+                    onFacetLongClicked(id);
+                    setCurrentFacet(id);
+                    return true;
+                }
+            });
+        } else {
+            button.setLongClickable(false);
+        }
+        return button;
+    }
+
+    private void startActivity(Intent intent) {
+        if (mActivityStarter != null && intent != null) {
+            mActivityStarter.startActivity(intent, true);
+        }
+    }
+
+    private void onFacetClicked(int index) {
+        // TODO: determine what data to pass to the trampoline, so it can start
+        // the default app or the lens picker.
+        startActivity(mIntents.get(index));
+    }
+
+    private void onFacetLongClicked(int index) {
+        // TODO: determine what data to pass to the trampoline, so it can start
+        // the default app or the lens picker.
+        startActivity(mLongPressIntents.get(index));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
new file mode 100644
index 0000000..efc3646
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.R.color;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NavigationBarView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A custom navigation bar for the automotive use case.
+ * <p>
+ * The navigation bar in the automotive use case is more like a list of shortcuts, rendered
+ * in a linear layout.
+ */
+class CarNavigationBarView extends NavigationBarView {
+    private LinearLayout mNavButtons;
+    private LinearLayout mLightsOutButtons;
+
+    public CarNavigationBarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        mNavButtons = (LinearLayout) findViewById(R.id.nav_buttons);
+        mLightsOutButtons = (LinearLayout) findViewById(R.id.lights_out);
+    }
+
+    public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){
+        mNavButtons.addView(button);
+        mLightsOutButtons.addView(lightsOutButton);
+    }
+
+    @Override
+    public void setDisabledFlags(int disabledFlags, boolean force) {
+        // TODO: Populate.
+    }
+
+    @Override
+    public void reorient() {
+        // We expect the car head unit to always have a fixed rotation so we ignore this. The super
+        // class implentation expects mRotatedViews to be populated, so if you call into it, there
+        // is a possibility of a NullPointerException.
+    }
+
+    @Override
+    public View getCurrentView() {
+        return this;
+    }
+
+    @Override
+    public void setNavigationIconHints(int hints, boolean force) {
+        // We do not need to set the navigation icon hints for a vehicle
+        // Calling setNavigationIconHints in the base class will result in a NPE as the car
+        // navigation bar does not have a back button.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
new file mode 100644
index 0000000..36b3a8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.R;
+
+/**
+ * A wrapper view for a car navigation facet, which includes a button icon and a drop down icon.
+ */
+public class CarNavigationButton extends RelativeLayout {
+    private static final float SELECTED_ALPHA = 1;
+    private static final float UNSELECTED_ALPHA = 0.7f;
+
+    private AlphaOptimizedImageButton mIcon;
+    private AlphaOptimizedImageButton mMoreIcon;
+
+    public CarNavigationButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon);
+        mIcon.setClickable(false);
+        mIcon.setScaleType(ImageView.ScaleType.CENTER);
+        mIcon.setBackgroundColor(android.R.color.transparent);
+        mIcon.setAlpha(UNSELECTED_ALPHA);
+
+        mMoreIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_more_icon);
+        mMoreIcon.setClickable(false);
+        mMoreIcon.setScaleType(ImageView.ScaleType.CENTER);
+        mMoreIcon.setBackgroundColor(android.R.color.transparent);
+        mMoreIcon.setVisibility(INVISIBLE);
+        mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
+        mMoreIcon.setAlpha(UNSELECTED_ALPHA);
+    }
+
+    public void setResources(Drawable icon) {
+        mIcon.setImageDrawable(icon);
+    }
+
+    public void setSelected(boolean selected, boolean showMoreIcon) {
+        if (selected) {
+            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE);
+            mMoreIcon.setAlpha(SELECTED_ALPHA);
+            mIcon.setAlpha(SELECTED_ALPHA);
+        } else {
+            mMoreIcon.setVisibility(INVISIBLE);
+            mIcon.setAlpha(UNSELECTED_ALPHA);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
new file mode 100644
index 0000000..f6f1f94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.app.ActivityManager;
+import android.app.ITaskStackListener;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+/**
+ * A status bar (and navigation bar) tailored for the automotive use case.
+ */
+public class CarStatusBar extends PhoneStatusBar {
+    private SystemServicesProxy mSystemServicesProxy;
+    private TaskStackListenerImpl mTaskStackListener;
+    private Handler mHandler;
+
+    private CarNavigationBarView mCarNavigationBar;
+    private CarNavigationBarController mController;
+
+    @Override
+    public void start() {
+        super.start();
+        mHandler = new Handler();
+        mTaskStackListener = new TaskStackListenerImpl(mHandler);
+        mSystemServicesProxy = new SystemServicesProxy(mContext);
+        mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+    }
+
+    @Override
+    protected void addNavigationBar() {
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+                PixelFormat.TRANSLUCENT);
+        lp.setTitle("CarNavigationBar");
+        lp.windowAnimations = 0;
+        mWindowManager.addView(mNavigationBarView, lp);
+    }
+
+    @Override
+    protected void createNavigationBarView(Context context) {
+        if (mNavigationBarView != null) {
+            return;
+        }
+        mCarNavigationBar =
+                (CarNavigationBarView) View.inflate(context, R.layout.car_navigation_bar, null);
+        mController = new CarNavigationBarController(context, mCarNavigationBar,
+                this /* ActivityStarter*/);
+        mNavigationBarView = mCarNavigationBar;
+    }
+
+    @Override
+    protected void repositionNavigationBar() {
+        // The navigation bar for a vehicle will not need to be repositioned, as it is always
+        // set at the bottom.
+    }
+
+    /**
+     * An implementation of ITaskStackListener, that listens for changes in the system task
+     * stack and notifies the navigation bar.
+     */
+    private class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
+        private Handler mHandler;
+
+        public TaskStackListenerImpl(Handler handler) {
+            this.mHandler = handler;
+        }
+
+        @Override
+        public void onActivityPinned() {
+        }
+
+        @Override
+        public void onTaskStackChanged() {
+            mHandler.removeCallbacks(this);
+            mHandler.post(this);
+        }
+
+        @Override
+        public void run() {
+            ensureMainThread();
+            SystemServicesProxy ssp = Recents.getSystemServices();
+            ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
+            mController.taskChanged(runningTaskInfo.baseActivity.getPackageName());
+        }
+
+        private void ensureMainThread() {
+            if (!Looper.getMainLooper().isCurrentThread()) {
+                throw new RuntimeException("Must be called on the UI thread");
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 99436a1..347ba3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -41,13 +41,13 @@
  */
 public class KeyguardBouncer {
 
-    private Context mContext;
-    private ViewMediatorCallback mCallback;
-    private LockPatternUtils mLockPatternUtils;
-    private ViewGroup mContainer;
+    protected Context mContext;
+    protected ViewMediatorCallback mCallback;
+    protected LockPatternUtils mLockPatternUtils;
+    protected ViewGroup mContainer;
     private StatusBarWindowManager mWindowManager;
-    private KeyguardHostView mKeyguardView;
-    private ViewGroup mRoot;
+    protected KeyguardHostView mKeyguardView;
+    protected ViewGroup mRoot;
     private boolean mShowingSoon;
     private int mBouncerPromptReason;
     private FalsingManager mFalsingManager;
@@ -134,7 +134,7 @@
     public void hide(boolean destroyView) {
         mFalsingManager.onBouncerHidden();
         cancelShowRunnable();
-         if (mKeyguardView != null) {
+        if (mKeyguardView != null) {
             mKeyguardView.cancelDismissAction();
             mKeyguardView.cleanUp();
         }
@@ -184,13 +184,13 @@
         mBouncerPromptReason = mCallback.getBouncerPromptReason();
     }
 
-    private void ensureView() {
+    protected void ensureView() {
         if (mRoot == null) {
             inflateView();
         }
     }
 
-    private void inflateView() {
+    protected void inflateView() {
         removeView();
         mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
         mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view);
@@ -201,7 +201,7 @@
         mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
     }
 
-    private void removeView() {
+    protected void removeView() {
         if (mRoot != null && mRoot.getParent() == mContainer) {
             mContainer.removeView(mRoot);
             mRoot = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index b93fc76..f41e47b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -166,7 +166,7 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
         // could not care less
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 58c9722..d0c14f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -746,7 +746,7 @@
         // Launch or bring the activity to front.
         final IActivityManager iAm = ActivityManagerNative.getDefault();
         try {
-            iAm.startActivityFromRecents(taskPersistentId, INVALID_STACK_ID, null /* options */);
+            iAm.startActivityFromRecents(taskPersistentId, null /* options */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Exception when activating a recent task", e);
         } catch (IllegalArgumentException e) {
@@ -1104,6 +1104,10 @@
                 }
             });
         }
+
+        @Override
+        public void onActivityPinned() {
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 79bd626..2db0804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -23,12 +23,14 @@
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
+import android.view.View;
 import android.view.ViewConfiguration;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.stackdivider.Divider;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.systemui.tuner.TunerService;
 
 import static android.view.WindowManager.*;
@@ -54,6 +56,7 @@
     private RecentsComponent mRecentsComponent;
     private Divider mDivider;
     private Context mContext;
+    private NavigationBarView mNavigationBarView;
     private boolean mIsVertical;
     private boolean mIsRTL;
 
@@ -62,6 +65,7 @@
     private final int mMinFlingVelocity;
     private int mTouchDownX;
     private int mTouchDownY;
+    private boolean mDownOnRecents;
     private VelocityTracker mVelocityTracker;
 
     private boolean mDockWindowEnabled;
@@ -78,9 +82,11 @@
         TunerService.get(context).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
     }
 
-    public void setComponents(RecentsComponent recentsComponent, Divider divider) {
+    public void setComponents(RecentsComponent recentsComponent, Divider divider,
+            NavigationBarView navigationBarView) {
         mRecentsComponent = recentsComponent;
         mDivider = divider;
+        mNavigationBarView = navigationBarView;
     }
 
     public void setBarState(boolean isVertical, boolean isRTL) {
@@ -156,6 +162,11 @@
         mDockWindowTouchSlopExceeded = false;
         mTouchDownX = (int) event.getX();
         mTouchDownY = (int) event.getY();
+        View recentsButton = mNavigationBarView.getRecentsButton();
+        mDownOnRecents = mTouchDownX >= recentsButton.getLeft()
+                && mTouchDownX <= recentsButton.getRight()
+                && mTouchDownY >= recentsButton.getTop()
+                && mTouchDownY <= recentsButton.getBottom();
     }
 
     private boolean handleDragActionMoveEvent(MotionEvent event) {
@@ -164,16 +175,19 @@
         int y = (int) event.getY();
         int xDiff = Math.abs(x - mTouchDownX);
         int yDiff = Math.abs(y - mTouchDownY);
+        if (mDivider == null || mRecentsComponent == null) {
+            return false;
+        }
         if (!mDockWindowTouchSlopExceeded) {
             boolean touchSlopExceeded = !mIsVertical
                     ? yDiff > mScrollTouchSlop && yDiff > xDiff
                     : xDiff > mScrollTouchSlop && xDiff > yDiff;
-            if (touchSlopExceeded && mDivider.getView().getWindowManagerProxy().getDockSide()
-                    == DOCKED_INVALID) {
-                mDragMode = calculateDragMode();
+            if (mDownOnRecents && touchSlopExceeded
+                    && mDivider.getView().getWindowManagerProxy().getDockSide() == DOCKED_INVALID) {
                 Rect initialBounds = null;
+                int dragMode = calculateDragMode();
                 int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-                if (mDragMode == DRAG_MODE_DIVIDER) {
+                if (dragMode == DRAG_MODE_DIVIDER) {
                     initialBounds = new Rect();
                     mDivider.getView().calculateBoundsForPosition(mIsVertical
                                     ? (int) event.getRawX()
@@ -182,24 +196,30 @@
                                     ? DOCKED_TOP
                                     : DOCKED_LEFT,
                             initialBounds);
-                } else if (mDragMode == DRAG_MODE_RECENTS && mTouchDownX
+                } else if (dragMode == DRAG_MODE_RECENTS && mTouchDownX
                         < mContext.getResources().getDisplayMetrics().widthPixels / 2) {
                     createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
                 }
-                mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS, createMode,
-                        initialBounds);
-                if (mDragMode == DRAG_MODE_DIVIDER) {
-                    mDivider.getView().startDragging();
+                boolean docked = mRecentsComponent.dockTopTask(dragMode == DRAG_MODE_RECENTS,
+                        createMode, initialBounds);
+                if (docked) {
+                    mDragMode = dragMode;
+                    if (mDragMode == DRAG_MODE_DIVIDER) {
+                        mDivider.getView().startDragging(false /* animate */);
+                    }
+                    mDockWindowTouchSlopExceeded = true;
+                    MetricsLogger.action(mContext,
+                            MetricsLogger.ACTION_WINDOW_DOCK_SWIPE);
+
+                    return true;
                 }
-                mDockWindowTouchSlopExceeded = true;
-                MetricsLogger.action(mContext,
-                        MetricsLogger.ACTION_WINDOW_DOCK_SWIPE);
-                return true;
             }
         } else {
             if (mDragMode == DRAG_MODE_DIVIDER) {
-                mDivider.getView().resizeStack(
-                        !mIsVertical ? (int) event.getRawY() : (int) event.getRawX());
+                int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX();
+                SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm()
+                        .calculateSnapTarget(position, 0f /* velocity */);
+                mDivider.getView().resizeStack(position, snapTarget.position, snapTarget);
             } else if (mDragMode == DRAG_MODE_RECENTS) {
                 mRecentsComponent.onDraggingInRecents(event.getRawY());
             }
@@ -210,7 +230,7 @@
     private void handleDragActionUpEvent(MotionEvent event) {
         mVelocityTracker.addMovement(event);
         mVelocityTracker.computeCurrentVelocity(1000);
-        if (mDockWindowTouchSlopExceeded) {
+        if (mDockWindowTouchSlopExceeded && mDivider != null && mRecentsComponent != null) {
             if (mDragMode == DRAG_MODE_DIVIDER) {
                 mDivider.getView().stopDragging(mIsVertical
                                 ? (int) event.getRawX()
@@ -250,7 +270,7 @@
         float absVelY = Math.abs(velocityY);
         boolean isValidFling = absVelX > mMinFlingVelocity &&
                 mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
-        if (isValidFling) {
+        if (isValidFling && mRecentsComponent != null) {
             boolean showNext;
             if (!mIsRTL) {
                 showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cddb1fc..6c0c0ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -36,11 +36,13 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.Gravity;
+import android.view.IDockedStackListener.Stub;
 import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -67,7 +69,6 @@
     View mCurrentView = null;
     View[] mRotatedViews = new View[4];
 
-    int mBarSize;
     boolean mVertical;
     boolean mScreenOn;
 
@@ -76,6 +77,9 @@
     int mNavigationIconHints = 0;
 
     private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
+    private Drawable mBackCarModeIcon, mBackLandCarModeIcon;
+    private Drawable mBackAltCarModeIcon, mBackAltLandCarModeIcon;
+    private Drawable mHomeDefaultIcon, mHomeCarModeIcon;
     private Drawable mRecentIcon;
     private Drawable mRecentLandIcon;
 
@@ -94,6 +98,7 @@
     private boolean mIsLayoutRtl;
     private boolean mLayoutTransitionsEnabled = true;
     private boolean mWakeAndUnlocking;
+    private boolean mCarMode = false;
 
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
@@ -155,8 +160,8 @@
                     final String how = "" + m.obj;
                     final int w = getWidth();
                     final int h = getHeight();
-                    final int vw = mCurrentView.getWidth();
-                    final int vh = mCurrentView.getHeight();
+                    final int vw = getCurrentView().getWidth();
+                    final int vh = getCurrentView().getHeight();
 
                     if (h != vh || w != vw) {
                         Log.w(TAG, String.format(
@@ -174,16 +179,15 @@
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mDisplay = ((WindowManager)context.getSystemService(
+        mDisplay = ((WindowManager) context.getSystemService(
                 Context.WINDOW_SERVICE)).getDefaultDisplay();
 
         final Resources res = getContext().getResources();
-        mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
         mVertical = false;
         mShowMenu = false;
         mGestureHelper = new NavigationBarGestureHelper(context);
 
-        getIcons(res);
+        getIcons(context);
 
         mBarTransitions = new NavigationBarTransitions(this);
     }
@@ -193,7 +197,7 @@
     }
 
     public void setComponents(RecentsComponent recentsComponent, Divider divider) {
-        mGestureHelper.setComponents(recentsComponent, divider);
+        mGestureHelper.setComponents(recentsComponent, divider, this);
     }
 
     public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
@@ -227,42 +231,54 @@
         return mCurrentView;
     }
 
-    public View getRecentsButton() {
-        return mCurrentView.findViewById(R.id.recent_apps);
+    public KeyButtonView getRecentsButton() {
+        return (KeyButtonView) getCurrentView().findViewById(R.id.recent_apps);
     }
 
     public View getMenuButton() {
-        return mCurrentView.findViewById(R.id.menu);
+        return getCurrentView().findViewById(R.id.menu);
     }
 
     public View getBackButton() {
-        return mCurrentView.findViewById(R.id.back);
+        return getCurrentView().findViewById(R.id.back);
     }
 
     public KeyButtonView getHomeButton() {
-        return (KeyButtonView) mCurrentView.findViewById(R.id.home);
+        return (KeyButtonView) getCurrentView().findViewById(R.id.home);
     }
 
     public View getImeSwitchButton() {
-        return mCurrentView.findViewById(R.id.ime_switcher);
+        return getCurrentView().findViewById(R.id.ime_switcher);
     }
 
     public View getAppShelf() {
-        return mCurrentView.findViewById(R.id.app_shelf);
+        return getCurrentView().findViewById(R.id.app_shelf);
     }
 
-    private void getIcons(Resources res) {
-        mBackIcon = res.getDrawable(R.drawable.ic_sysbar_back);
+    private void getCarModeIcons(Context ctx) {
+        mBackCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_carmode);
+        mBackLandCarModeIcon = mBackCarModeIcon;
+        mBackAltCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime_carmode);
+        mBackAltLandCarModeIcon = mBackAltCarModeIcon;
+        mHomeCarModeIcon = ctx.getDrawable(R.drawable.ic_sysbar_home_carmode);
+    }
+
+    private void getIcons(Context ctx) {
+        mBackIcon = ctx.getDrawable(R.drawable.ic_sysbar_back);
         mBackLandIcon = mBackIcon;
-        mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
+        mBackAltIcon = ctx.getDrawable(R.drawable.ic_sysbar_back_ime);
         mBackAltLandIcon = mBackAltIcon;
-        mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
+
+        mHomeDefaultIcon = ctx.getDrawable(R.drawable.ic_sysbar_home);
+
+        mRecentIcon = ctx.getDrawable(R.drawable.ic_sysbar_recent);
         mRecentLandIcon = mRecentIcon;
+        getCarModeIcons(ctx);
     }
 
     @Override
     public void setLayoutDirection(int layoutDirection) {
-        getIcons(getContext().getResources());
+        getIcons(getContext());
 
         super.setLayoutDirection(layoutDirection);
     }
@@ -276,6 +292,18 @@
         setNavigationIconHints(hints, false);
     }
 
+    private Drawable getBackIconWithAlt(boolean carMode, boolean landscape) {
+        return landscape
+                ? carMode ? mBackAltLandCarModeIcon : mBackAltLandIcon
+                : carMode ? mBackAltCarModeIcon : mBackAltIcon;
+    }
+
+    private Drawable getBackIcon(boolean carMode, boolean landscape) {
+        return landscape
+                ? carMode ? mBackLandCarModeIcon : mBackLandIcon
+                : carMode ? mBackCarModeIcon : mBackIcon;
+    }
+
     public void setNavigationIconHints(int hints, boolean force) {
         if (!force && hints == mNavigationIconHints) return;
         final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
@@ -290,11 +318,23 @@
 
         mNavigationIconHints = hints;
 
-        ((ImageView)getBackButton()).setImageDrawable(backAlt
-                ? (mVertical ? mBackAltLandIcon : mBackAltIcon)
-                : (mVertical ? mBackLandIcon : mBackIcon));
+        // We have to replace or restore the back and home button icons when exiting or entering
+        // carmode, respectively. Recents are not available in CarMode in nav bar so change
+        // to recent icon is not required.
+        Drawable backIcon = (backAlt)
+                ? getBackIconWithAlt(mCarMode, mVertical)
+                : getBackIcon(mCarMode, mVertical);
 
-        ((ImageView)getRecentsButton()).setImageDrawable(mVertical ? mRecentLandIcon : mRecentIcon);
+        ((ImageView) getBackButton()).setImageDrawable(backIcon);
+
+        ((ImageView) getRecentsButton()).setImageDrawable(
+                mVertical ? mRecentLandIcon : mRecentIcon);
+
+        if (mCarMode) {
+            ((ImageView) getHomeButton()).setImageDrawable(mHomeCarModeIcon);
+        } else {
+            ((ImageView) getHomeButton()).setImageDrawable(mHomeDefaultIcon);
+        }
 
         final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
         getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
@@ -324,7 +364,7 @@
             setSlippery(disableHome && disableRecent && disableBack && disableSearch);
         }
 
-        ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+        ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
         if (navButtons != null) {
             LayoutTransition lt = navButtons.getLayoutTransition();
             if (lt != null) {
@@ -377,7 +417,7 @@
 
     private void updateLayoutTransitionsEnabled() {
         boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
-        ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
+        ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
         LayoutTransition lt = navButtons.getLayoutTransition();
         if (lt != null) {
             if (enabled) {
@@ -455,6 +495,32 @@
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
 
         updateRTLOrder();
+
+        try {
+            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(new Stub() {
+                @Override
+                public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
+                }
+
+                @Override
+                public void onDockedStackExistsChanged(final boolean exists) throws RemoteException {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            updateRecentsIcon(exists);
+                        }
+                    });
+                }
+            });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed registering docked stack exists listener", e);
+        }
+    }
+
+    private void updateRecentsIcon(boolean dockedStackExists) {
+        getRecentsButton().setImageResource(dockedStackExists
+                ? R.drawable.ic_sysbar_docked
+                : R.drawable.ic_sysbar_recent);
     }
 
     public boolean isVertical() {
@@ -518,8 +584,33 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        boolean uiCarModeChanged = updateCarMode(newConfig);
         updateRTLOrder();
         updateTaskSwitchHelper();
+        if (uiCarModeChanged) {
+            // uiMode changed either from carmode or to carmode.
+            // replace the nav bar button icons based on which mode
+            // we are switching to.
+            setNavigationIconHints(mNavigationIconHints, true);
+        }
+    }
+
+    /**
+     * If the configuration changed, update the carmode and return that it was updated.
+     */
+    private boolean updateCarMode(Configuration newConfig) {
+        boolean uiCarModeChanged = false;
+        if (newConfig != null) {
+            int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
+            if (mCarMode && uiMode != Configuration.UI_MODE_TYPE_CAR) {
+                mCarMode = false;
+                uiCarModeChanged = true;
+            } else if (uiMode == Configuration.UI_MODE_TYPE_CAR) {
+                mCarMode = true;
+                uiCarModeChanged = true;
+            }
+        }
+        return uiCarModeChanged;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
new file mode 100644
index 0000000..405ef05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -0,0 +1,151 @@
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A controller for the space in the status bar to the left of the system icons. This area is
+ * normally reserved for notifications.
+ */
+public class NotificationIconAreaController {
+    private final NotificationColorUtil mNotificationColorUtil;
+
+    private int mIconSize;
+    private int mIconHPadding;
+    private int mIconTint = Color.WHITE;
+
+    private PhoneStatusBar mPhoneStatusBar;
+    protected View mNotificationIconArea;
+    private IconMerger mNotificationIcons;
+    private ImageView mMoreIcon;
+
+    public NotificationIconAreaController(Context context, PhoneStatusBar phoneStatusBar) {
+        mPhoneStatusBar = phoneStatusBar;
+        mNotificationColorUtil = NotificationColorUtil.getInstance(context);
+
+        initializeNotificationAreaViews(context);
+    }
+
+    /**
+     * Initializes the views that will represent the notification area.
+     */
+    protected void initializeNotificationAreaViews(Context context) {
+        Resources res = context.getResources();
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+        mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+
+        LayoutInflater layoutInflater = LayoutInflater.from(context);
+        mNotificationIconArea = layoutInflater.inflate(R.layout.notification_icon_area, null);
+
+        mMoreIcon = (ImageView) mNotificationIconArea.findViewById(R.id.moreIcon);
+        mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
+
+        mNotificationIcons =
+                (IconMerger) mNotificationIconArea.findViewById(R.id.notificationIcons);
+        mNotificationIcons.setOverflowIndicator(mMoreIcon);
+    }
+
+    /**
+     * Returns the view that represents the notification area.
+     */
+    public View getNotificationInnerAreaView() {
+        return mNotificationIconArea;
+    }
+
+    /**
+     * Sets the color that should be used to tint any icons in the notification area. If this
+     * method is not called, the default tint is {@link Color#WHITE}.
+     */
+    public void setIconTint(int iconTint) {
+        mIconTint = iconTint;
+        mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
+        applyNotificationIconsTint();
+    }
+
+    /**
+     * Updates the notifications with the given list of notifications to display.
+     */
+    public void updateNotificationIcons(NotificationData notificationData) {
+        final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+                mIconSize + 2 * mIconHPadding, mPhoneStatusBar.getStatusBarHeight());
+
+        ArrayList<NotificationData.Entry> activeNotifications =
+                notificationData.getActiveNotifications();
+        final int size = activeNotifications.size();
+        ArrayList<StatusBarIconView> toShow = new ArrayList<>(size);
+
+        // Filter out ambient notifications and notification children.
+        for (int i = 0; i < size; i++) {
+            NotificationData.Entry ent = activeNotifications.get(i);
+            if (notificationData.isAmbient(ent.key)
+                    && !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
+                continue;
+            }
+            if (!PhoneStatusBar.isTopLevelChild(ent)) {
+                continue;
+            }
+            toShow.add(ent.icon);
+        }
+
+        ArrayList<View> toRemove = new ArrayList<>();
+        for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
+            View child = mNotificationIcons.getChildAt(i);
+            if (!toShow.contains(child)) {
+                toRemove.add(child);
+            }
+        }
+
+        final int toRemoveCount = toRemove.size();
+        for (int i = 0; i < toRemoveCount; i++) {
+            mNotificationIcons.removeView(toRemove.get(i));
+        }
+
+        for (int i = 0; i < toShow.size(); i++) {
+            View v = toShow.get(i);
+            if (v.getParent() == null) {
+                mNotificationIcons.addView(v, i, params);
+            }
+        }
+
+        // Re-sort notification icons
+        final int childCount = mNotificationIcons.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View actual = mNotificationIcons.getChildAt(i);
+            StatusBarIconView expected = toShow.get(i);
+            if (actual == expected) {
+                continue;
+            }
+            mNotificationIcons.removeView(expected);
+            mNotificationIcons.addView(expected, i);
+        }
+
+        applyNotificationIconsTint();
+    }
+
+    /**
+     * Applies {@link #mIconTint} to the notification icons.
+     */
+    private void applyNotificationIconsTint() {
+        for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
+            StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
+            boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
+            boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
+            if (colorize) {
+                v.setImageTintList(ColorStateList.valueOf(mIconTint));
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 42fd872..ba20679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1318,7 +1318,6 @@
         mHeader.setExpansion(getHeaderExpansionFraction());
         setQsTranslation(height);
         requestScrollerTopPaddingUpdate(false /* animate */);
-        updateNotificationScrim(height);
         if (mKeyguardShowing) {
             updateHeaderKeyguard();
         }
@@ -1355,12 +1354,6 @@
         }
     }
 
-    private void updateNotificationScrim(float height) {
-        int startDistance = mQsMinExpansionHeight + mNotificationScrimWaitDistance;
-        float progress = (height - startDistance) / (mQsMaxExpansionHeight - startDistance);
-        progress = Math.max(0.0f, Math.min(progress, 1.0f));
-    }
-
     private float getHeaderExpansionFraction() {
         if (!mKeyguardShowing) {
             return getQsExpansionFraction();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index e1a400d..f2c57e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -23,8 +23,6 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-import java.util.ArrayList;
-
 public abstract class PanelBar extends FrameLayout {
     public static final boolean DEBUG = false;
     public static final String TAG = PanelBar.class.getSimpleName();
@@ -39,14 +37,10 @@
     public static final int STATE_OPENING = 1;
     public static final int STATE_OPEN = 2;
 
-    PanelHolder mPanelHolder;
-    ArrayList<PanelView> mPanels = new ArrayList<PanelView>();
-    PanelView mTouchingPanel;
+    PanelView mPanel;
     private int mState = STATE_CLOSED;
     private boolean mTracking;
 
-    float mPanelExpandedFractionSum;
-
     public void go(int state) {
         if (DEBUG) LOG("go state: %d -> %d", mState, state);
         mState = state;
@@ -61,55 +55,28 @@
         super.onFinishInflate();
     }
 
-    public void addPanel(PanelView pv) {
-        mPanels.add(pv);
+    public void setPanel(PanelView pv) {
+        mPanel = pv;
         pv.setBar(this);
     }
 
-    public void setPanelHolder(PanelHolder ph) {
-        if (ph == null) {
-            Log.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
-            return;
-        }
-        ph.setBar(this);
-        mPanelHolder = ph;
-        final int N = ph.getChildCount();
-        for (int i=0; i<N; i++) {
-            final View v = ph.getChildAt(i);
-            if (v != null && v instanceof PanelView) {
-                addPanel((PanelView) v);
-            }
-        }
-    }
-
     public void setBouncerShowing(boolean showing) {
         int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 
         setImportantForAccessibility(important);
 
-        if (mPanelHolder != null) {
-            mPanelHolder.setImportantForAccessibility(important);
-        }
+        if (mPanel != null) mPanel.setImportantForAccessibility(important);
     }
 
-    public float getBarHeight() {
-        return getMeasuredHeight();
-    }
-
-    public PanelView selectPanelForTouch(MotionEvent touch) {
-        final int N = mPanels.size();
-        return mPanels.get((int)(N * touch.getX() / getMeasuredWidth()));
-    }
-
-    public boolean panelsEnabled() {
+    public boolean panelEnabled() {
         return true;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // Allow subclasses to implement enable/disable semantics
-        if (!panelsEnabled()) {
+        if (!panelEnabled()) {
             if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
                         (int) event.getX(), (int) event.getY()));
@@ -117,14 +84,12 @@
             return false;
         }
 
-        // figure out which panel needs to be talked to here
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final PanelView panel = selectPanelForTouch(event);
+            final PanelView panel = mPanel;
             if (panel == null) {
                 // panel is not there, so we'll eat the gesture
                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
                         (int) event.getX(), (int) event.getY()));
-                mTouchingPanel = null;
                 return true;
             }
             boolean enabled = panel.isEnabled();
@@ -135,90 +100,65 @@
                 Log.v(TAG, String.format(
                         "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
                         panel, (int) event.getX(), (int) event.getY()));
-                mTouchingPanel = null;
                 return true;
             }
-            startOpeningPanel(panel);
         }
-        final boolean result = mTouchingPanel != null
-                ? mTouchingPanel.onTouchEvent(event)
-                : true;
-        return result;
-    }
-
-    // called from PanelView when self-expanding, too
-    public void startOpeningPanel(PanelView panel) {
-        if (DEBUG) LOG("startOpeningPanel: " + panel);
-        mTouchingPanel = panel;
-        mPanelHolder.setSelectedPanel(mTouchingPanel);
-        for (PanelView pv : mPanels) {
-            if (pv != panel) {
-                pv.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-            }
-        }
+        return mPanel == null || mPanel.onTouchEvent(event);
     }
 
     public abstract void panelScrimMinFractionChanged(float minFraction);
 
     /**
-     * @param panel the panel which changed its expansion state
      * @param frac the fraction from the expansion in [0, 1]
      * @param expanded whether the panel is currently expanded; this is independent from the
      *                 fraction as the panel also might be expanded if the fraction is 0
      */
-    public void panelExpansionChanged(PanelView panel, float frac, boolean expanded) {
+    public void panelExpansionChanged(float frac, boolean expanded) {
         boolean fullyClosed = true;
-        PanelView fullyOpenedPanel = null;
-        if (SPEW) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
-        mPanelExpandedFractionSum = 0f;
-        for (PanelView pv : mPanels) {
-            pv.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
-            // adjust any other panels that may be partially visible
-            if (expanded) {
-                if (mState == STATE_CLOSED) {
-                    go(STATE_OPENING);
-                    onPanelPeeked();
-                }
-                fullyClosed = false;
-                final float thisFrac = pv.getExpandedFraction();
-                mPanelExpandedFractionSum += thisFrac;
-                if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
-                if (panel == pv) {
-                    if (thisFrac == 1f) fullyOpenedPanel = panel;
-                }
+        boolean fullyOpened = false;
+        if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
+        PanelView pv = mPanel;
+        pv.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
+        // adjust any other panels that may be partially visible
+        if (expanded) {
+            if (mState == STATE_CLOSED) {
+                go(STATE_OPENING);
+                onPanelPeeked();
             }
+            fullyClosed = false;
+            final float thisFrac = pv.getExpandedFraction();
+            if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
+            fullyOpened = thisFrac >= 1f;
         }
-        mPanelExpandedFractionSum /= mPanels.size();
-        if (fullyOpenedPanel != null && !mTracking) {
+        if (fullyOpened && !mTracking) {
             go(STATE_OPEN);
-            onPanelFullyOpened(fullyOpenedPanel);
+            onPanelFullyOpened();
         } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
             go(STATE_CLOSED);
-            onAllPanelsCollapsed();
+            onPanelCollapsed();
         }
 
         if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
-                (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
+                fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
     }
 
-    public void collapseAllPanels(boolean animate, boolean delayed, float speedUpFactor) {
+    public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
         boolean waiting = false;
-        for (PanelView pv : mPanels) {
-            if (animate && !pv.isFullyCollapsed()) {
-                pv.collapse(delayed, speedUpFactor);
-                waiting = true;
-            } else {
-                pv.resetViews();
-                pv.setExpandedFraction(0); // just in case
-                pv.cancelPeek();
-            }
+        PanelView pv = mPanel;
+        if (animate && !pv.isFullyCollapsed()) {
+            pv.collapse(delayed, speedUpFactor);
+            waiting = true;
+        } else {
+            pv.resetViews();
+            pv.setExpandedFraction(0); // just in case
+            pv.cancelPeek();
         }
-        if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
+        if (DEBUG) LOG("collapsePanel: animate=%s waiting=%s", animate, waiting);
         if (!waiting && mState != STATE_CLOSED) {
             // it's possible that nothing animated, so we replicate the termination
             // conditions of panelExpansionChanged here
             go(STATE_CLOSED);
-            onAllPanelsCollapsed();
+            onPanelCollapsed();
         }
     }
 
@@ -226,19 +166,19 @@
         if (DEBUG) LOG("onPanelPeeked");
     }
 
-    public void onAllPanelsCollapsed() {
-        if (DEBUG) LOG("onAllPanelsCollapsed");
+    public void onPanelCollapsed() {
+        if (DEBUG) LOG("onPanelCollapsed");
     }
 
-    public void onPanelFullyOpened(PanelView openPanel) {
+    public void onPanelFullyOpened() {
         if (DEBUG) LOG("onPanelFullyOpened");
     }
 
-    public void onTrackingStarted(PanelView panel) {
+    public void onTrackingStarted() {
         mTracking = true;
     }
 
-    public void onTrackingStopped(PanelView panel, boolean expand) {
+    public void onTrackingStopped(boolean expand) {
         mTracking = false;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
deleted file mode 100644
index d7f34d5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.EventLog;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-import com.android.systemui.EventLogTags;
-
-public class PanelHolder extends FrameLayout {
-    public static final boolean DEBUG_GESTURES = true;
-
-    private int mSelectedPanelIndex = -1;
-    private PanelBar mBar;
-
-    public PanelHolder(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setChildrenDrawingOrderEnabled(true);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        setChildrenDrawingOrderEnabled(true);
-    }
-
-    public int getPanelIndex(PanelView pv) {
-        final int N = getChildCount();
-        for (int i=0; i<N; i++) {
-            final PanelView v = (PanelView) getChildAt(i);
-            if (pv == v) return i;
-        }
-        return -1;
-    }
-
-    public void setSelectedPanel(PanelView pv) {
-        mSelectedPanelIndex = getPanelIndex(pv);
-    }
-
-    @Override
-    protected int getChildDrawingOrder(int childCount, int i) {
-        if (mSelectedPanelIndex == -1) {
-            return i;
-        } else {
-            if (i == childCount - 1) {
-                return mSelectedPanelIndex;
-            } else if (i >= mSelectedPanelIndex) {
-                return i + 1;
-            } else {
-                return i;
-            }
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (DEBUG_GESTURES) {
-            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
-                EventLog.writeEvent(EventLogTags.SYSUI_PANELHOLDER_TOUCH,
-                        event.getActionMasked(), (int) event.getX(), (int) event.getY());
-            }
-        }
-        return false;
-    }
-
-    public void setBar(PanelBar panelBar) {
-        mBar = panelBar;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 5e54ba7..aa01bf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -58,7 +58,6 @@
 
     private float mPeekHeight;
     private float mHintDistance;
-    private int mEdgeTapAreaWidth;
     private float mInitialOffsetOnTouch;
     private boolean mCollapsedAndHeadsUpOnDown;
     private float mExpandedFraction = 0;
@@ -202,7 +201,6 @@
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
         mHintDistance = res.getDimension(R.dimen.hint_move_distance);
-        mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width);
         mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
     }
 
@@ -434,7 +432,7 @@
 
     protected void onTrackingStopped(boolean expand) {
         mTracking = false;
-        mBar.onTrackingStopped(PanelView.this, expand);
+        mBar.onTrackingStopped(expand);
         notifyBarPanelExpansionChanged();
     }
 
@@ -442,7 +440,7 @@
         endClosing();
         mTracking = true;
         mCollapseAfterPeek = false;
-        mBar.onTrackingStarted(PanelView.this);
+        mBar.onTrackingStarted();
         notifyExpandingStarted();
         notifyBarPanelExpansionChanged();
     }
@@ -895,7 +893,6 @@
                                 != mStatusBar.getStatusBarHeight()) {
                             getViewTreeObserver().removeOnGlobalLayoutListener(this);
                             if (animate) {
-                                mBar.startOpeningPanel(PanelView.this);
                                 notifyExpandingStarted();
                                 fling(0, true /* expand */);
                             } else {
@@ -1027,7 +1024,7 @@
     }
 
     protected void notifyBarPanelExpansionChanged() {
-        mBar.panelExpansionChanged(this, mExpandedFraction, mExpandedFraction > 0f || mPeekPending
+        mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f || mPeekPending
                 || mPeekAnimator != null || mInstantExpanding || isPanelVisibleBecauseOfHeadsUp()
                 || mTracking || mHeightAnimator != null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 80fcba60e..78497ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -81,7 +81,6 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.ThreadedRenderer;
-import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -95,7 +94,6 @@
 import android.view.animation.PathInterpolator;
 import android.widget.ImageView;
 import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -152,7 +150,6 @@
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.statusbar.policy.RemoteInputView;
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -283,9 +280,9 @@
     VolumeComponent mVolumeComponent;
     KeyguardUserSwitcher mKeyguardUserSwitcher;
     FlashlightController mFlashlightController;
-    UserSwitcherController mUserSwitcherController;
+    protected UserSwitcherController mUserSwitcherController;
     NextAlarmController mNextAlarmController;
-    KeyguardMonitor mKeyguardMonitor;
+    protected KeyguardMonitor mKeyguardMonitor;
     BrightnessMirrorController mBrightnessMirrorController;
     AccessibilityController mAccessibilityController;
     FullscreenUserSwitcher mFullscreenUserSwitcher;
@@ -299,7 +296,7 @@
     StatusBarWindowView mStatusBarWindow;
     PhoneStatusBarView mStatusBarView;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
-    private StatusBarWindowManager mStatusBarWindowManager;
+    protected StatusBarWindowManager mStatusBarWindowManager;
     private UnlockMethodCache mUnlockMethodCache;
     private DozeServiceHost mDozeServiceHost;
     private boolean mWakeUpComingFromTouch;
@@ -345,7 +342,6 @@
 
     // Tracking finger for opening/closing.
     boolean mTracking;
-    VelocityTracker mVelocityTracker;
 
     int[] mAbsPos = new int[2];
     ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -427,8 +423,8 @@
     private int mMaxKeyguardNotifications;
 
     private ViewMediatorCallback mKeyguardViewMediatorCallback;
-    private ScrimController mScrimController;
-    private DozeScrimController mDozeScrimController;
+    protected ScrimController mScrimController;
+    protected DozeScrimController mDozeScrimController;
 
     private final Runnable mAutohide = new Runnable() {
         @Override
@@ -442,7 +438,7 @@
     private boolean mWaitingForKeyguardExit;
     private boolean mDozing;
     private boolean mDozingRequested;
-    private boolean mScrimSrcModeEnabled;
+    protected boolean mScrimSrcModeEnabled;
 
     private Interpolator mLinearInterpolator = new LinearInterpolator();
     private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
@@ -637,8 +633,8 @@
         addNavigationBar();
 
         // Lastly, call to the icon policy to install/update all the icons.
-        mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController,
-                mUserInfoController, mBluetoothController);
+        mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCastController,
+                mHotspotController, mUserInfoController, mBluetoothController);
         mIconPolicy.setCurrentUserSetup(mUserSetup);
         mSettingsObserver.onChange(false); // set up
 
@@ -695,16 +691,14 @@
             }
         });
 
-        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
-        mStatusBarView.setBar(this);
-
-        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
-        mStatusBarView.setPanelHolder(holder);
-
         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
                 R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
 
+        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
+        mStatusBarView.setBar(this);
+        mStatusBarView.setPanel(mNotificationPanel);
+
         if (!ActivityManager.isHighEndGfx()) {
             mStatusBarWindow.setBackground(null);
             mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(
@@ -728,32 +722,7 @@
             boolean showNav = mWindowManagerService.hasNavigationBar();
             if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
             if (showNav) {
-                // Optionally show app shortcuts in the nav bar "shelf" area.
-                if (shouldShowAppShelf()) {
-                    mNavigationBarView = (NavigationBarView) View.inflate(
-                            context, R.layout.navigation_bar_with_apps, null);
-                } else {
-                    mNavigationBarView = (NavigationBarView) View.inflate(
-                            context, R.layout.navigation_bar, null);
-                }
-                mNavigationBarView.setDisabledFlags(mDisabled1);
-                mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
-                mNavigationBarView.setOnVerticalChangedListener(
-                        new NavigationBarView.OnVerticalChangedListener() {
-                    @Override
-                    public void onVerticalChanged(boolean isVertical) {
-                        if (mAssistManager != null) {
-                            mAssistManager.onConfigurationChanged();
-                        }
-                        mNotificationPanel.setQsScrimEnabled(!isVertical);
-                    }
-                });
-                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
-                    @Override
-                    public boolean onTouch(View v, MotionEvent event) {
-                        checkUserAutohide(v, event);
-                        return false;
-                    }});
+                createNavigationBarView(context);
             }
         } catch (RemoteException ex) {
             // no window manager? good luck with that
@@ -842,10 +811,10 @@
         mBatteryController = new BatteryController(mContext);
         mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
             @Override
-            public void onPowerSaveChanged() {
+            public void onPowerSaveChanged(boolean isPowerSave) {
                 mHandler.post(mCheckBarModes);
                 if (mDozeServiceHost != null) {
-                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
+                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
                 }
             }
             @Override
@@ -898,7 +867,7 @@
         mKeyguardMonitor = new KeyguardMonitor(mContext);
         if (UserManager.get(mContext).isUserSwitcherEnabled()) {
             mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
-                    mHandler);
+                    mHandler, this);
             if (mUserSwitcherController.useFullscreenUserSwitcher()) {
                 mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, mUserSwitcherController,
                         (ViewStub) mStatusBarWindow.findViewById(
@@ -919,7 +888,7 @@
                     mNetworkController, mZenModeController, mHotspotController,
                     mCastController, mFlashlightController,
                     mUserSwitcherController, mUserInfoController, mKeyguardMonitor,
-                    mSecurityController, mBatteryController);
+                    mSecurityController, mBatteryController, mIconController);
             mQSPanel.setHost(qsh);
             mQSPanel.setTiles(qsh.getTiles());
             mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
@@ -979,6 +948,35 @@
         return mStatusBarView;
     }
 
+    protected void createNavigationBarView(Context context) {
+    // Optionally show app shortcuts in the nav bar "shelf" area.
+        if (shouldShowAppShelf()) {
+            mNavigationBarView = (NavigationBarView) View.inflate(
+                    context, R.layout.navigation_bar_with_apps, null);
+        } else {
+            mNavigationBarView = (NavigationBarView) View.inflate(
+                    context, R.layout.navigation_bar, null);
+        }
+        mNavigationBarView.setDisabledFlags(mDisabled1);
+        mNavigationBarView.setComponents(mRecents, getComponent(Divider.class));
+        mNavigationBarView.setOnVerticalChangedListener(
+                new NavigationBarView.OnVerticalChangedListener() {
+            @Override
+            public void onVerticalChanged(boolean isVertical) {
+                if (mAssistManager != null) {
+                    mAssistManager.onConfigurationChanged();
+                }
+                mNotificationPanel.setQsScrimEnabled(!isVertical);
+            }
+        });
+        mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                checkUserAutohide(v, event);
+                return false;
+            }});
+    }
+
     /** Returns true if the app shelf should be shown in the nav bar. */
     private boolean shouldShowAppShelf() {
         // Allow adb to override the default shelf behavior:
@@ -1075,17 +1073,18 @@
         }
     }
 
-    private void startKeyguard() {
+    protected void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mFingerprintUnlockController = new FingerprintUnlockController(mContext,
                 mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
                 mScrimController, this);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
-                mStatusBarWindow, mStatusBarWindowManager, mScrimController,
+                getBouncerContainer(), mStatusBarWindowManager, mScrimController,
                 mFingerprintUnlockController);
         mKeyguardIndicationController.setStatusBarKeyguardViewManager(
                 mStatusBarKeyguardViewManager);
         mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+        mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
     }
 
@@ -1098,6 +1097,10 @@
         return mStatusBarWindow;
     }
 
+    protected ViewGroup getBouncerContainer() {
+        return mStatusBarWindow;
+    }
+
     public int getStatusBarHeight() {
         if (mNaturalBarHeight < 0) {
             final Resources res = mContext.getResources();
@@ -1126,12 +1129,14 @@
         @Override
         public boolean onLongClick(View v) {
             if (mRecents != null) {
-                mRecents.dockTopTask(false /* draggingInRecents */,
+                boolean docked = mRecents.dockTopTask(false /* draggingInRecents */,
                         ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
                         null /* initialBounds */);
-                MetricsLogger.action(mContext,
-                        MetricsLogger.ACTION_WINDOW_DOCK_LONGPRESS);
-                return true;
+                if (docked) {
+                    MetricsLogger.action(mContext,
+                            MetricsLogger.ACTION_WINDOW_DOCK_LONGPRESS);
+                    return true;
+                }
             }
             return false;
         }
@@ -1191,7 +1196,7 @@
     }
 
     // For small-screen devices (read: phones) that lack hardware navigation buttons
-    private void addNavigationBar() {
+    protected void addNavigationBar() {
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
         if (mNavigationBarView == null) return;
 
@@ -1200,7 +1205,7 @@
         mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
     }
 
-    private void repositionNavigationBar() {
+    protected void repositionNavigationBar() {
         if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
 
         prepareNavigationBarView();
@@ -1234,17 +1239,14 @@
         return lp;
     }
 
-    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
-        mIconController.addSystemIcon(slot, index, viewIndex, icon);
+    @Override
+    public void setIcon(String slot, StatusBarIcon icon) {
+        mIconController.setIcon(slot, icon);
     }
 
-    public void updateIcon(String slot, int index, int viewIndex,
-            StatusBarIcon old, StatusBarIcon icon) {
-        mIconController.updateSystemIcon(slot, index, viewIndex, old, icon);
-    }
-
-    public void removeIcon(String slot, int index, int viewIndex) {
-        mIconController.removeSystemIcon(slot, index, viewIndex);
+    @Override
+    public void removeIcon(String slot) {
+        mIconController.removeIcon(slot);
     }
 
     public UserHandle getCurrentUserHandle() {
@@ -1269,19 +1271,26 @@
         }
 
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            // Stop screensaver if the notification has a full-screen intent.
-            // (like an incoming phone call)
-            awakenDreams();
+            if (mNotificationData.shouldSuppressScreenOn(notification.getKey())) {
+                if (DEBUG) {
+                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
+                }
+            } else {
+                // Stop screensaver if the notification has a full-screen intent.
+                // (like an incoming phone call)
+                awakenDreams();
 
-            // not immersive & a full-screen alert should be shown
-            if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
-            try {
-                EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
-                        notification.getKey());
-                notification.getNotification().fullScreenIntent.send();
-                shadeEntry.notifyFullScreenIntentLaunched();
-                MetricsLogger.count(mContext, "note_fullscreen", 1);
-            } catch (PendingIntent.CanceledException e) {
+                // not immersive & a full-screen alert should be shown
+                if (DEBUG)
+                    Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+                try {
+                    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
+                            notification.getKey());
+                    notification.getNotification().fullScreenIntent.send();
+                    shadeEntry.notifyFullScreenIntentLaunched();
+                    MetricsLogger.count(mContext, "note_fullscreen", 1);
+                } catch (PendingIntent.CanceledException e) {
+                }
             }
         }
         addNotificationViews(shadeEntry, ranking);
@@ -2269,7 +2278,7 @@
             mStatusBarWindowManager.setStatusBarFocusable(false);
 
             mStatusBarWindow.cancelExpandHelper();
-            mStatusBarView.collapseAllPanels(true /* animate */, delayed, speedUpFactor);
+            mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
         }
     }
 
@@ -2318,7 +2327,7 @@
 
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
-            mStatusBarView.collapseAllPanels(true, false /* delayed */, 1.0f /* speedUpFactor */);
+            mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
         }
     }
 
@@ -2331,7 +2340,7 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/,
+        mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
                 1.0f /* speedUpFactor */);
 
         mNotificationPanel.closeQs();
@@ -2423,7 +2432,7 @@
             mStatusBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
             if (!showing && mState == StatusBarState.SHADE) {
-                mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */,
+                mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
                         1.0f /* speedUpFactor */);
             }
         }
@@ -2980,6 +2989,7 @@
             if (DEBUG) Log.v(TAG, "onReceive: " + intent);
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+                getKeyboardShortcuts().dismissKeyboardShortcutsDialog();
                 if (isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");
@@ -4120,32 +4130,6 @@
         return false;
     }
 
-    // Recents
-
-    @Override
-    protected void showRecents(boolean triggeredFromAltTab) {
-        // Set the recents visibility flag
-        mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
-        notifyUiVisibilityChanged(mSystemUiVisibility);
-        super.showRecents(triggeredFromAltTab);
-    }
-
-    @Override
-    protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-        // Unset the recents visibility flag
-        mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
-        notifyUiVisibilityChanged(mSystemUiVisibility);
-        super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
-    }
-
-    @Override
-    protected void toggleRecents() {
-        // Toggle the recents visibility flag
-        mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
-        notifyUiVisibilityChanged(mSystemUiVisibility);
-        super.toggleRecents();
-    }
-
     public void updateRecentsVisibility(boolean visible) {
         // Update the recents visibility flag
         if (visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b89cd22..59d831c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -20,7 +20,6 @@
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.AlarmManager.AlarmClockInfo;
-import android.app.StatusBarManager;
 import android.app.SynchronousUserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -35,7 +34,6 @@
 import android.provider.Settings.Global;
 import android.telecom.TelecomManager;
 import android.util.Log;
-
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.systemui.R;
@@ -66,13 +64,13 @@
     private static final String SLOT_MANAGED_PROFILE = "managed_profile";
 
     private final Context mContext;
-    private final StatusBarManager mService;
     private final Handler mHandler = new Handler();
     private final CastController mCast;
     private final HotspotController mHotspot;
     private final AlarmManager mAlarmManager;
     private final UserInfoController mUserInfoController;
     private final UserManager mUserManager;
+    private final StatusBarIconController mIconController;
 
     // Assume it's all good unless we hear otherwise.  We don't always seem
     // to get broadcasts that it *is* there.
@@ -97,18 +95,14 @@
             String action = intent.getAction();
             if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
                 updateAlarm();
-            }
-            else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
+            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
                     action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
                 updateVolumeZen();
-            }
-            else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+            } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
                 updateSimState(intent);
-            }
-            else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
+            } else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
                 updateTTY(intent);
-            }
-            else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED)) {
+            } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED)) {
                 updateQuietState();
                 updateManagedProfile();
             }
@@ -119,18 +113,19 @@
         @Override
         public void run() {
             if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
-            mService.setIconVisibility(SLOT_CAST, false);
+            mIconController.setIconVisibility(SLOT_CAST, false);
         }
     };
 
-    public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot,
-            UserInfoController userInfoController, BluetoothController bluetooth) {
+    public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
+            CastController cast, HotspotController hotspot, UserInfoController userInfoController,
+            BluetoothController bluetooth) {
         mContext = context;
+        mIconController = iconController;
         mCast = cast;
         mHotspot = hotspot;
         mBluetooth = bluetooth;
         mBluetooth.addStateChangedCallback(this);
-        mService = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mUserInfoController = userInfoController;
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -153,40 +148,40 @@
         }
 
         // TTY status
-        mService.setIcon(SLOT_TTY,  R.drawable.stat_sys_tty_mode, 0, null);
-        mService.setIconVisibility(SLOT_TTY, false);
+        mIconController.setIcon(SLOT_TTY,  R.drawable.stat_sys_tty_mode, null);
+        mIconController.setIconVisibility(SLOT_TTY, false);
 
         // bluetooth status
         updateBluetooth();
 
         // Alarm clock
-        mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
-        mService.setIconVisibility(SLOT_ALARM_CLOCK, false);
+        mIconController.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, null);
+        mIconController.setIconVisibility(SLOT_ALARM_CLOCK, false);
 
         // zen
-        mService.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, 0, null);
-        mService.setIconVisibility(SLOT_ZEN, false);
+        mIconController.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, null);
+        mIconController.setIconVisibility(SLOT_ZEN, false);
 
         // volume
-        mService.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_vibrate, 0, null);
-        mService.setIconVisibility(SLOT_VOLUME, false);
+        mIconController.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_vibrate, null);
+        mIconController.setIconVisibility(SLOT_VOLUME, false);
         updateVolumeZen();
 
         // cast
-        mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0, null);
-        mService.setIconVisibility(SLOT_CAST, false);
+        mIconController.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, null);
+        mIconController.setIconVisibility(SLOT_CAST, false);
         mCast.addCallback(mCastCallback);
 
         // hotspot
-        mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0,
+        mIconController.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot,
                 mContext.getString(R.string.accessibility_status_bar_hotspot));
-        mService.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled());
+        mIconController.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled());
         mHotspot.addCallback(mHotspotCallback);
 
         // managed profile
-        mService.setIcon(SLOT_MANAGED_PROFILE, R.drawable.stat_sys_managed_profile_status, 0,
+        mIconController.setIcon(SLOT_MANAGED_PROFILE, R.drawable.stat_sys_managed_profile_status,
                 mContext.getString(R.string.accessibility_managed_profile));
-        mService.setIconVisibility(SLOT_MANAGED_PROFILE, mManagedProfileIconVisible);
+        mIconController.setIconVisibility(SLOT_MANAGED_PROFILE, mManagedProfileIconVisible);
     }
 
     public void setZenMode(int zen) {
@@ -198,32 +193,27 @@
         final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
         final boolean zenNone = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS;
-        mService.setIcon(SLOT_ALARM_CLOCK, zenNone ? R.drawable.stat_sys_alarm_dim
-                : R.drawable.stat_sys_alarm, 0, null);
-        mService.setIconVisibility(SLOT_ALARM_CLOCK, mCurrentUserSetup && hasAlarm);
+        mIconController.setIcon(SLOT_ALARM_CLOCK, zenNone ? R.drawable.stat_sys_alarm_dim
+                : R.drawable.stat_sys_alarm, null);
+        mIconController.setIconVisibility(SLOT_ALARM_CLOCK, mCurrentUserSetup && hasAlarm);
     }
 
     private final void updateSimState(Intent intent) {
         String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
         if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
             mSimState = IccCardConstants.State.ABSENT;
-        }
-        else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
+        } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
             mSimState = IccCardConstants.State.CARD_IO_ERROR;
-        }
-        else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+        } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
             mSimState = IccCardConstants.State.READY;
-        }
-        else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+        } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
             final String lockedReason =
                     intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
             if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
                 mSimState = IccCardConstants.State.PIN_REQUIRED;
-            }
-            else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+            } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
                 mSimState = IccCardConstants.State.PUK_REQUIRED;
-            }
-            else {
+            } else {
                 mSimState = IccCardConstants.State.NETWORK_LOCKED;
             }
         } else {
@@ -270,18 +260,18 @@
         }
 
         if (zenVisible) {
-            mService.setIcon(SLOT_ZEN, zenIconId, 0, zenDescription);
+            mIconController.setIcon(SLOT_ZEN, zenIconId, zenDescription);
         }
         if (zenVisible != mZenVisible) {
-            mService.setIconVisibility(SLOT_ZEN, zenVisible);
+            mIconController.setIconVisibility(SLOT_ZEN, zenVisible);
             mZenVisible = zenVisible;
         }
 
         if (volumeVisible) {
-            mService.setIcon(SLOT_VOLUME, volumeIconId, 0, volumeDescription);
+            mIconController.setIcon(SLOT_VOLUME, volumeIconId, volumeDescription);
         }
         if (volumeVisible != mVolumeVisible) {
-            mService.setIconVisibility(SLOT_VOLUME, volumeVisible);
+            mIconController.setIconVisibility(SLOT_VOLUME, volumeVisible);
             mVolumeVisible = volumeVisible;
         }
         updateAlarm();
@@ -310,8 +300,8 @@
             }
         }
 
-        mService.setIcon(SLOT_BLUETOOTH, iconId, 0, contentDescription);
-        mService.setIconVisibility(SLOT_BLUETOOTH, bluetoothEnabled);
+        mIconController.setIcon(SLOT_BLUETOOTH, iconId, contentDescription);
+        mIconController.setIconVisibility(SLOT_BLUETOOTH, bluetoothEnabled);
     }
 
     private final void updateTTY(Intent intent) {
@@ -324,13 +314,13 @@
         if (enabled) {
             // TTY is on
             if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
-            mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0,
+            mIconController.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode,
                     mContext.getString(R.string.accessibility_tty_enabled));
-            mService.setIconVisibility(SLOT_TTY, true);
+            mIconController.setIconVisibility(SLOT_TTY, true);
         } else {
             // TTY is off
             if (DEBUG) Log.v(TAG, "updateTTY: set TTY off");
-            mService.setIconVisibility(SLOT_TTY, false);
+            mIconController.setIconVisibility(SLOT_TTY, false);
         }
     }
 
@@ -346,9 +336,9 @@
         if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
         mHandler.removeCallbacks(mRemoveCastIconRunnable);
         if (isCasting) {
-            mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0,
+            mIconController.setIcon(SLOT_CAST, R.drawable.stat_sys_cast,
                     mContext.getString(R.string.accessibility_casting));
-            mService.setIconVisibility(SLOT_CAST, true);
+            mIconController.setIconVisibility(SLOT_CAST, true);
         } else {
             // don't turn off the screen-record icon for a few seconds, just to make sure the user
             // has seen it
@@ -392,17 +382,19 @@
         final boolean showIcon;
         if (mManagedProfileFocused && !mKeyguardVisible) {
             showIcon = true;
-            mService.setIcon(SLOT_MANAGED_PROFILE, R.drawable.stat_sys_managed_profile_status, 0,
+            mIconController.setIcon(SLOT_MANAGED_PROFILE,
+                    R.drawable.stat_sys_managed_profile_status,
                     mContext.getString(R.string.accessibility_managed_profile));
         } else if (mManagedProfileInQuietMode) {
             showIcon = true;
-            mService.setIcon(SLOT_MANAGED_PROFILE, R.drawable.stat_sys_managed_profile_status_off, 0,
+            mIconController.setIcon(SLOT_MANAGED_PROFILE,
+                    R.drawable.stat_sys_managed_profile_status_off,
                     mContext.getString(R.string.accessibility_managed_profile));
         } else {
             showIcon = false;
         }
         if (mManagedProfileIconVisible != showIcon) {
-            mService.setIconVisibility(SLOT_MANAGED_PROFILE, showIcon);
+            mIconController.setIconVisibility(SLOT_MANAGED_PROFILE, showIcon);
             mManagedProfileIconVisible = showIcon;
         }
     }
@@ -431,7 +423,7 @@
     private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() {
         @Override
         public void onHotspotChanged(boolean enabled) {
-            mService.setIconVisibility(SLOT_HOTSPOT, enabled);
+            mIconController.setIconVisibility(SLOT_HOTSPOT, enabled);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c0887ca..813a167 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.view.MotionEvent;
@@ -26,7 +25,6 @@
 
 import com.android.systemui.DejankUtils;
 import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
 
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
@@ -35,8 +33,7 @@
 
     PhoneStatusBar mBar;
 
-    PanelView mLastFullyOpenedPanel = null;
-    PanelView mNotificationPanel;
+    boolean mIsFullyOpenedPanel = false;
     private final PhoneStatusBarTransitions mBarTransitions;
     private ScrimController mScrimController;
     private float mMinFraction;
@@ -51,7 +48,6 @@
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        Resources res = getContext().getResources();
         mBarTransitions = new PhoneStatusBarTransitions(this);
     }
 
@@ -73,15 +69,7 @@
     }
 
     @Override
-    public void addPanel(PanelView pv) {
-        super.addPanel(pv);
-        if (pv.getId() == R.id.notification_panel) {
-            mNotificationPanel = pv;
-        }
-    }
-
-    @Override
-    public boolean panelsEnabled() {
+    public boolean panelEnabled() {
         return mBar.panelsEnabled();
     }
 
@@ -101,25 +89,17 @@
     }
 
     @Override
-    public PanelView selectPanelForTouch(MotionEvent touch) {
-        // No double swiping. If either panel is open, nothing else can be pulled down.
-        return mNotificationPanel.getExpandedHeight() > 0
-                ? null
-                : mNotificationPanel;
-    }
-
-    @Override
     public void onPanelPeeked() {
         super.onPanelPeeked();
         mBar.makeExpandedVisible(false);
     }
 
     @Override
-    public void onAllPanelsCollapsed() {
-        super.onAllPanelsCollapsed();
+    public void onPanelCollapsed() {
+        super.onPanelCollapsed();
         // Close the status bar in the next frame so we can show the end of the animation.
         DejankUtils.postAfterTraversal(mHideExpandedRunnable);
-        mLastFullyOpenedPanel = null;
+        mIsFullyOpenedPanel = false;
     }
 
     public void removePendingHideExpandedRunnables() {
@@ -127,12 +107,12 @@
     }
 
     @Override
-    public void onPanelFullyOpened(PanelView openPanel) {
-        super.onPanelFullyOpened(openPanel);
-        if (openPanel != mLastFullyOpenedPanel) {
-            openPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+    public void onPanelFullyOpened() {
+        super.onPanelFullyOpened();
+        if (!mIsFullyOpenedPanel) {
+            mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
-        mLastFullyOpenedPanel = openPanel;
+        mIsFullyOpenedPanel = true;
     }
 
     @Override
@@ -151,8 +131,8 @@
     }
 
     @Override
-    public void onTrackingStarted(PanelView panel) {
-        super.onTrackingStarted(panel);
+    public void onTrackingStarted() {
+        super.onTrackingStarted();
         mBar.onTrackingStarted();
         mScrimController.onTrackingStarted();
     }
@@ -164,8 +144,8 @@
     }
 
     @Override
-    public void onTrackingStopped(PanelView panel, boolean expand) {
-        super.onTrackingStopped(panel, expand);
+    public void onTrackingStopped(boolean expand) {
+        super.onTrackingStopped(expand);
         mBar.onTrackingStopped(expand);
     }
 
@@ -189,8 +169,8 @@
     }
 
     @Override
-    public void panelExpansionChanged(PanelView panel, float frac, boolean expanded) {
-        super.panelExpansionChanged(panel, frac, expanded);
+    public void panelExpansionChanged(float frac, boolean expanded) {
+        super.panelExpansionChanged(frac, expanded);
         mPanelFraction = frac;
         updateScrimFraction();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index e6d837a..29ad5d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -18,30 +18,26 @@
 
 import android.app.ActivityManager;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
-import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
-import android.os.RemoteException;
 import android.provider.Settings;
-import android.service.quicksettings.IQSService;
-import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.View;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.TileServices;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
 import com.android.systemui.qs.tiles.BatteryTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
 import com.android.systemui.qs.tiles.ColorInversionTile;
-import com.android.systemui.qs.tiles.CustomTile;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.FlashlightTile;
 import com.android.systemui.qs.tiles.HotspotTile;
@@ -76,7 +72,7 @@
 import java.util.Map;
 
 /** Platform implementation of the quick settings tile host **/
-public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tunable {
+public final class QSTileHost implements QSTile.Host, Tunable {
     private static final String TAG = "QSTileHost";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -100,16 +96,20 @@
     private final KeyguardMonitor mKeyguard;
     private final SecurityController mSecurity;
     private final BatteryController mBattery;
+    private final StatusBarIconController mIconController;
+    private final TileServices mServices;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
+    private View mHeader;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
-            BluetoothController bluetooth, LocationController location,
-            RotationLockController rotation, NetworkController network,
-            ZenModeController zen, HotspotController hotspot,
-            CastController cast, FlashlightController flashlight,
-            UserSwitcherController userSwitcher, UserInfoController userInfo, KeyguardMonitor keyguard,
-            SecurityController security, BatteryController battery) {
+                      BluetoothController bluetooth, LocationController location,
+                      RotationLockController rotation, NetworkController network,
+                      ZenModeController zen, HotspotController hotspot,
+                      CastController cast, FlashlightController flashlight,
+                      UserSwitcherController userSwitcher, UserInfoController userInfo,
+                      KeyguardMonitor keyguard, SecurityController security,
+                      BatteryController battery, StatusBarIconController iconController) {
         mContext = context;
         mStatusBar = statusBar;
         mBluetooth = bluetooth;
@@ -125,15 +125,22 @@
         mKeyguard = keyguard;
         mSecurity = security;
         mBattery = battery;
+        mIconController = iconController;
 
         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
                 Process.THREAD_PRIORITY_BACKGROUND);
         ht.start();
         mLooper = ht.getLooper();
 
+        mServices = new TileServices(this, mLooper);
+
         TunerService.get(mContext).addTunable(this, TILES_SETTING);
     }
 
+    public void setHeaderView(View view) {
+        mHeader = view;
+    }
+
     public PhoneStatusBar getPhoneStatusBar() {
         return mStatusBar;
     }
@@ -172,6 +179,11 @@
         // already logged
     }
 
+    public void animateExpandQS() {
+        // TODO: Better path to animated panel expansion.
+        mHeader.performClick();
+    }
+
     @Override
     public void collapsePanels() {
         mStatusBar.postAnimateCollapsePanels();
@@ -256,6 +268,14 @@
         return mSecurity;
     }
 
+    public TileServices getTileServices() {
+        return mServices;
+    }
+
+    public StatusBarIconController getIconController() {
+        return mIconController;
+    }
+
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (!TILES_SETTING.equals(key)) {
@@ -306,50 +326,6 @@
                 TextUtils.join(",", specs), ActivityManager.getCurrentUser());
     }
 
-    @Override
-    public void updateQsTile(Tile tile) throws RemoteException {
-        verifyCaller(tile.getComponentName().getPackageName());
-        CustomTile customTile = getTileForComponent(tile.getComponentName());
-        if (customTile != null) {
-            customTile.updateState(tile);
-            customTile.refreshState();
-        }
-    }
-
-    @Override
-    public void onShowDialog(Tile tile) throws RemoteException {
-        verifyCaller(tile.getComponentName().getPackageName());
-        CustomTile customTile = getTileForComponent(tile.getComponentName());
-        if (customTile != null) {
-            customTile.onDialogShown();
-            collapsePanels();
-        }
-    }
-
-    private void verifyCaller(String packageName) {
-        try {
-            int uid = mContext.getPackageManager().getPackageUid(packageName,
-                    Binder.getCallingUserHandle().getIdentifier());
-            if (Binder.getCallingUid() != uid) {
-                throw new SecurityException("Component outside caller's uid");
-            }
-        } catch (NameNotFoundException e) {
-            throw new SecurityException(e);
-        }
-    }
-
-    private CustomTile getTileForComponent(ComponentName component) {
-        // TODO: Build map for easier lookup.
-        for (QSTile<?> qsTile : mTiles.values()) {
-            if (qsTile instanceof CustomTile) {
-                if (((CustomTile) qsTile).getComponent().equals(component)) {
-                    return (CustomTile) qsTile;
-                }
-            }
-        }
-        return null;
-    }
-
     public QSTile<?> createTile(String tileSpec) {
         if (tileSpec.equals("wifi")) return WifiTile.isSupported(this)
                 ? new WifiTile(this) : null;
@@ -374,7 +350,10 @@
         // Intent tiles.
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
-        else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
+        else {
+            Log.w(TAG, "Bad tile spec: " + tileSpec);
+            return null;
+        }
     }
 
     public static List<String> loadTileSpecs(Context context, String tileList) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 1372cca..5b44f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -24,7 +24,6 @@
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -135,7 +134,6 @@
     @Override
     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
         mNextAlarm = nextAlarm;
-        Log.d(TAG, "Got alarm update " + (nextAlarm != null));
         if (nextAlarm != null) {
             mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
         }
@@ -179,10 +177,8 @@
 
     private void updateListeners() {
         if (mListening) {
-            Log.d(TAG, "Listening for Alarms");
             mNextAlarmController.addStateChangedCallback(this);
         } else {
-            Log.d(TAG, "Not listening for Alarms");
             mNextAlarmController.removeStateChangedCallback(this);
         }
     }
@@ -201,23 +197,16 @@
         }
     }
 
-    public void setupHost(QSTileHost host) {
-        final QSTileHost myHost = new QSTileHost(host.getContext(), host.getPhoneStatusBar(),
-                host.getBluetoothController(), host.getLocationController(),
-                host.getRotationLockController(), host.getNetworkController(),
-                host.getZenModeController(), host.getHotspotController(),
-                host.getCastController(), host.getFlashlightController(),
-                host.getUserSwitcherController(), host.getUserInfoController(),
-                host.getKeyguardMonitor(), host.getSecurityController(),
-                host.getBatteryController());
+    public void setupHost(final QSTileHost host) {
+        host.setHeaderView(this);
         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
-        mHeaderQsPanel.setHost(myHost);
+        mHeaderQsPanel.setHost(host);
         mHeaderQsPanel.setMaxTiles(5);
-        mHeaderQsPanel.setTiles(myHost.getTiles());
-        myHost.addCallback(new QSTile.Host.Callback() {
+        mHeaderQsPanel.setTiles(host.getTiles());
+        host.addCallback(new QSTile.Host.Callback() {
             @Override
             public void onTilesChanged() {
-                mHeaderQsPanel.setTiles(myHost.getTiles());
+                mHeaderQsPanel.setTiles(host.getTiles());
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 3d21f44..d2f1ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -419,7 +419,7 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
         // could not care less
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index d5b980e..0eb0bf89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -21,9 +21,11 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.view.View;
@@ -33,16 +35,13 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -54,7 +53,7 @@
  * limited to: notification icons, signal cluster, additional status icons, and clock in the status
  * bar.
  */
-public class StatusBarIconController implements Tunable {
+public class StatusBarIconController extends StatusBarIconList implements Tunable {
 
     public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
 
@@ -65,15 +64,15 @@
     private Interpolator mLinearOutSlowIn;
     private Interpolator mFastOutSlowIn;
     private DemoStatusIcons mDemoStatusIcons;
-    private NotificationColorUtil mNotificationColorUtil;
 
     private LinearLayout mSystemIconArea;
     private LinearLayout mStatusIcons;
     private SignalClusterView mSignalCluster;
     private LinearLayout mStatusIconsKeyguard;
-    private IconMerger mNotificationIcons;
-    private View mNotificationIconArea;
-    private ImageView mMoreIcon;
+
+    private NotificationIconAreaController mNotificationIconAreaController;
+    private View mNotificationIconAreaInner;
+
     private BatteryMeterView mBatteryMeterView;
     private TextView mClock;
 
@@ -109,14 +108,19 @@
             PhoneStatusBar phoneStatusBar) {
         mContext = context;
         mPhoneStatusBar = phoneStatusBar;
-        mNotificationColorUtil = NotificationColorUtil.getInstance(context);
         mSystemIconArea = (LinearLayout) statusBar.findViewById(R.id.system_icon_area);
         mStatusIcons = (LinearLayout) statusBar.findViewById(R.id.statusIcons);
         mSignalCluster = (SignalClusterView) statusBar.findViewById(R.id.signal_cluster);
-        mNotificationIconArea = statusBar.findViewById(R.id.notification_icon_area_inner);
-        mNotificationIcons = (IconMerger) statusBar.findViewById(R.id.notificationIcons);
-        mMoreIcon = (ImageView) statusBar.findViewById(R.id.moreIcon);
-        mNotificationIcons.setOverflowIndicator(mMoreIcon);
+
+        mNotificationIconAreaController =
+                new NotificationIconAreaController(context, phoneStatusBar);
+        mNotificationIconAreaInner =
+                mNotificationIconAreaController.getNotificationInnerAreaView();
+
+        ViewGroup notificationIconArea =
+                (ViewGroup) statusBar.findViewById(R.id.notification_icon_area);
+        notificationIconArea.addView(mNotificationIconAreaInner);
+
         mStatusIconsKeyguard = (LinearLayout) keyguardStatusBar.findViewById(R.id.statusIcons);
         mBatteryMeterView = (BatteryMeterView) statusBar.findViewById(R.id.battery);
         mClock = (TextView) statusBar.findViewById(R.id.clock);
@@ -146,23 +150,27 @@
         }
         // Remove all the icons.
         for (int i = views.size() - 1; i >= 0; i--) {
-            removeSystemIcon(views.get(i).getSlot(), i, i);
+            removeIcon(views.get(i).getSlot());
         }
         // Add them all back
         for (int i = 0; i < views.size(); i++) {
-            addSystemIcon(views.get(i).getSlot(), i, i, views.get(i).getStatusBarIcon());
+            setIcon(views.get(i).getSlot(), views.get(i).getStatusBarIcon());
         }
-    };
+    }
 
     public void updateResources() {
         mIconSize = mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_icon_size);
         mIconHPadding = mContext.getResources().getDimensionPixelSize(
                 R.dimen.status_bar_icon_padding);
+        defineSlots(mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_statusBarIcons));
         FontSizeUtils.updateFontSize(mClock, R.dimen.status_bar_clock_size);
     }
 
-    public void addSystemIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+    private void addSystemIcon(int index, StatusBarIcon icon) {
+        String slot = getSlot(index);
+        int viewIndex = getViewIndex(index);
         boolean blocked = mIconBlacklist.contains(slot);
         StatusBarIconView view = new StatusBarIconView(mContext, slot, null, blocked);
         view.set(icon);
@@ -175,8 +183,77 @@
         applyIconTint();
     }
 
-    public void updateSystemIcon(String slot, int index, int viewIndex,
-            StatusBarIcon old, StatusBarIcon icon) {
+    public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
+        int index = getSlotIndex(slot);
+        StatusBarIcon icon = getIcon(index);
+        if (icon == null) {
+            icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
+                    Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);
+            setIcon(slot, icon);
+        } else {
+            icon.icon = Icon.createWithResource(mContext, resourceId);
+            icon.contentDescription = contentDescription;
+            handleSet(index, icon);
+        }
+    }
+
+    public void setExternalIcon(String slot) {
+        int viewIndex = getViewIndex(getSlotIndex(slot));
+        ImageView imageView = (ImageView) mStatusIcons.getChildAt(viewIndex);
+        imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+        imageView.setAdjustViewBounds(true);
+        imageView = (ImageView) mStatusIconsKeyguard.getChildAt(viewIndex);
+        imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+        imageView.setAdjustViewBounds(true);
+    }
+
+    public void setIcon(String slot, StatusBarIcon icon) {
+        setIcon(getSlotIndex(slot), icon);
+    }
+
+    public void removeIcon(String slot) {
+        int index = getSlotIndex(slot);
+        removeIcon(index);
+    }
+
+    public void setIconVisibility(String slot, boolean visibility) {
+        int index = getSlotIndex(slot);
+        StatusBarIcon icon = getIcon(index);
+        if (icon == null || icon.visible == visibility) {
+            return;
+        }
+        icon.visible = visibility;
+        handleSet(index, icon);
+    }
+
+    @Override
+    public void removeIcon(int index) {
+        if (getIcon(index) == null) {
+            return;
+        }
+        super.removeIcon(index);
+        int viewIndex = getViewIndex(index);
+        mStatusIcons.removeViewAt(viewIndex);
+        mStatusIconsKeyguard.removeViewAt(viewIndex);
+    }
+
+    @Override
+    public void setIcon(int index, StatusBarIcon icon) {
+        if (icon == null) {
+            removeIcon(index);
+            return;
+        }
+        boolean isNew = getIcon(index) == null;
+        super.setIcon(index, icon);
+        if (isNew) {
+            addSystemIcon(index, icon);
+        } else {
+            handleSet(index, icon);
+        }
+    }
+
+    private void handleSet(int index, StatusBarIcon icon) {
+        int viewIndex = getViewIndex(index);
         StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
         view.set(icon);
         view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
@@ -184,66 +261,8 @@
         applyIconTint();
     }
 
-    public void removeSystemIcon(String slot, int index, int viewIndex) {
-        mStatusIcons.removeViewAt(viewIndex);
-        mStatusIconsKeyguard.removeViewAt(viewIndex);
-    }
-
     public void updateNotificationIcons(NotificationData notificationData) {
-        final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
-                mIconSize + 2*mIconHPadding, mPhoneStatusBar.getStatusBarHeight());
-
-        ArrayList<NotificationData.Entry> activeNotifications =
-                notificationData.getActiveNotifications();
-        final int N = activeNotifications.size();
-        ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
-
-        // Filter out ambient notifications and notification children.
-        for (int i = 0; i < N; i++) {
-            NotificationData.Entry ent = activeNotifications.get(i);
-            if (notificationData.isAmbient(ent.key)
-                    && !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
-                continue;
-            }
-            if (!PhoneStatusBar.isTopLevelChild(ent)) {
-                continue;
-            }
-            toShow.add(ent.icon);
-        }
-
-        ArrayList<View> toRemove = new ArrayList<>();
-        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
-            View child = mNotificationIcons.getChildAt(i);
-            if (!toShow.contains(child)) {
-                toRemove.add(child);
-            }
-        }
-
-        final int toRemoveCount = toRemove.size();
-        for (int i = 0; i < toRemoveCount; i++) {
-            mNotificationIcons.removeView(toRemove.get(i));
-        }
-
-        for (int i=0; i<toShow.size(); i++) {
-            View v = toShow.get(i);
-            if (v.getParent() == null) {
-                mNotificationIcons.addView(v, i, params);
-            }
-        }
-
-        // Resort notification icons
-        final int childCount = mNotificationIcons.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View actual = mNotificationIcons.getChildAt(i);
-            StatusBarIconView expected = toShow.get(i);
-            if (actual == expected) {
-                continue;
-            }
-            mNotificationIcons.removeView(expected);
-            mNotificationIcons.addView(expected, i);
-        }
-
-        applyNotificationIconsTint();
+        mNotificationIconAreaController.updateNotificationIcons(notificationData);
     }
 
     public void hideSystemIconArea(boolean animate) {
@@ -255,11 +274,11 @@
     }
 
     public void hideNotificationIconArea(boolean animate) {
-        animateHide(mNotificationIconArea, animate);
+        animateHide(mNotificationIconAreaInner, animate);
     }
 
     public void showNotificationIconArea(boolean animate) {
-        animateShow(mNotificationIconArea, animate);
+        animateShow(mNotificationIconAreaInner, animate);
     }
 
     public void setClockVisibility(boolean visible) {
@@ -375,6 +394,7 @@
         mDarkIntensity = darkIntensity;
         mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
                 mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone);
+        mNotificationIconAreaController.setIconTint(mIconTint);
         applyIconTint();
     }
 
@@ -392,21 +412,8 @@
             v.setImageTintList(ColorStateList.valueOf(mIconTint));
         }
         mSignalCluster.setIconTint(mIconTint, mDarkIntensity);
-        mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
         mBatteryMeterView.setDarkIntensity(mDarkIntensity);
         mClock.setTextColor(mIconTint);
-        applyNotificationIconsTint();
-    }
-
-    private void applyNotificationIconsTint() {
-        for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
-            StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
-            boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
-            boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
-            if (colorize) {
-                v.setImageTintList(ColorStateList.valueOf(mIconTint));
-            }
-        }
     }
 
     public void appTransitionPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
new file mode 100644
index 0000000..62d6b76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.internal.statusbar.StatusBarIcon;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class StatusBarIconList {
+    private ArrayList<String> mSlots = new ArrayList<>();
+    private ArrayList<StatusBarIcon> mIcons = new ArrayList<>();
+
+    public void defineSlots(String[] slots) {
+        final int N = slots.length;
+        for (int i=0; i < N; i++) {
+            mSlots.add(slots[i]);
+            mIcons.add(null);
+        }
+    }
+
+    public int getSlotIndex(String slot) {
+        final int N = mSlots.size();
+        for (int i=0; i<N; i++) {
+            if (slot.equals(mSlots.get(i))) {
+                return i;
+            }
+        }
+        // Auto insert new items at the beginning.
+        mSlots.add(0, slot);
+        mIcons.add(0, null);
+        return 0;
+    }
+
+    public int size() {
+        return mSlots.size();
+    }
+
+    public void setIcon(int index, StatusBarIcon icon) {
+        mIcons.set(index, icon);
+    }
+
+    public void removeIcon(int index) {
+        mIcons.set(index, null);
+    }
+
+    public String getSlot(int index) {
+        return mSlots.get(index);
+    }
+
+    public StatusBarIcon getIcon(int index) {
+        return mIcons.get(index);
+    }
+
+    public int getViewIndex(int index) {
+        int count = 0;
+        for (int i = 0; i < index; i++) {
+            if (mIcons.get(i) != null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public void dump(PrintWriter pw) {
+        final int N = mSlots.size();
+        pw.println("Icon list:");
+        for (int i=0; i<N; i++) {
+            pw.printf("  %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i));
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 05f6e57..7a05b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -30,7 +30,9 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.RemoteInputController;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 
@@ -40,7 +42,7 @@
  * which is in turn, reported to this class by the current
  * {@link com.android.keyguard.KeyguardViewBase}.
  */
-public class StatusBarKeyguardViewManager {
+public class StatusBarKeyguardViewManager implements RemoteInputController.Callback {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = -3 * 16;
@@ -53,11 +55,11 @@
 
     private static String TAG = "StatusBarKeyguardViewManager";
 
-    private final Context mContext;
+    protected final Context mContext;
 
-    private LockPatternUtils mLockPatternUtils;
-    private ViewMediatorCallback mViewMediatorCallback;
-    private PhoneStatusBar mPhoneStatusBar;
+    protected LockPatternUtils mLockPatternUtils;
+    protected ViewMediatorCallback mViewMediatorCallback;
+    protected PhoneStatusBar mPhoneStatusBar;
     private ScrimController mScrimController;
     private FingerprintUnlockController mFingerprintUnlockController;
 
@@ -66,15 +68,18 @@
 
     private boolean mDeviceInteractive = false;
     private boolean mScreenTurnedOn;
-    private KeyguardBouncer mBouncer;
-    private boolean mShowing;
-    private boolean mOccluded;
+    protected KeyguardBouncer mBouncer;
+    protected boolean mShowing;
+    protected boolean mOccluded;
+    protected boolean mRemoteInputActive;
 
-    private boolean mFirstUpdate = true;
-    private boolean mLastShowing;
-    private boolean mLastOccluded;
+    protected boolean mFirstUpdate = true;
+    protected boolean mLastShowing;
+    protected boolean mLastOccluded;
     private boolean mLastBouncerShowing;
     private boolean mLastBouncerDismissible;
+    protected boolean mLastRemoteInputActive;
+
     private OnDismissAction mAfterKeyguardGoneAction;
     private boolean mDeviceWillWakeUp;
     private boolean mDeferScrimFadeOut;
@@ -95,8 +100,8 @@
         mStatusBarWindowManager = statusBarWindowManager;
         mScrimController = scrimController;
         mFingerprintUnlockController = fingerprintUnlockController;
-        mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
-                mStatusBarWindowManager, container);
+        mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
+                mViewMediatorCallback, mLockPatternUtils, mStatusBarWindowManager, container);
     }
 
     /**
@@ -114,7 +119,7 @@
      * Shows the notification keyguard or the bouncer depending on
      * {@link KeyguardBouncer#needsFullscreenBouncer()}.
      */
-    private void showBouncerOrKeyguard() {
+    protected void showBouncerOrKeyguard() {
         if (mBouncer.needsFullscreenBouncer()) {
 
             // The keyguard might be showing (already). So we need to hide it.
@@ -199,6 +204,12 @@
         mPhoneStatusBar.onScreenTurnedOn();
     }
 
+    @Override
+    public void onRemoteInputActive(boolean active) {
+        mRemoteInputActive = active;
+        updateStates();
+    }
+
     public void onScreenTurnedOff() {
         mScreenTurnedOn = false;
     }
@@ -423,24 +434,26 @@
         }
     };
 
-    private void updateStates() {
+    protected void updateStates() {
         int vis = mContainer.getSystemUiVisibility();
         boolean showing = mShowing;
         boolean occluded = mOccluded;
         boolean bouncerShowing = mBouncer.isShowing();
         boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
+        boolean remoteInputActive = mRemoteInputActive;
 
-        if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing)
+        if ((bouncerDismissible || !showing || remoteInputActive) !=
+                (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
                 || mFirstUpdate) {
-            if (bouncerDismissible || !showing) {
+            if (bouncerDismissible || !showing || remoteInputActive) {
                 mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
             } else {
                 mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
             }
         }
 
-        boolean navBarVisible = (!(showing && !occluded) || bouncerShowing);
-        boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing);
+        boolean navBarVisible = isNavBarVisible();
+        boolean lastNavBarVisible = getLastNavBarVisible();
         if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
             if (mPhoneStatusBar.getNavigationBarView() != null) {
                 if (navBarVisible) {
@@ -477,10 +490,25 @@
         mLastOccluded = occluded;
         mLastBouncerShowing = bouncerShowing;
         mLastBouncerDismissible = bouncerDismissible;
+        mLastRemoteInputActive = remoteInputActive;
 
         mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
+    /**
+     * @return Whether the navigation bar should be made visible based on the current state.
+     */
+    protected boolean isNavBarVisible() {
+        return !(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive;
+    }
+
+    /**
+     * @return Whether the navigation bar was made visible based on the last known state.
+     */
+    protected boolean getLastNavBarVisible() {
+        return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive;
+    }
+
     public boolean onMenuPressed() {
         return mBouncer.onMenuPressed();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index abe51ac..382de19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -30,6 +30,7 @@
 import com.android.keyguard.R;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.statusbar.BaseStatusBar;
+import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 
 import java.io.FileDescriptor;
@@ -39,7 +40,7 @@
 /**
  * Encapsulates all logic for the status bar window state management.
  */
-public class StatusBarWindowManager {
+public class StatusBarWindowManager implements RemoteInputController.Callback {
 
     private final Context mContext;
     private final WindowManager mWindowManager;
@@ -133,6 +134,12 @@
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         }
+
+        if (state.remoteInputActive) {
+            mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+        } else {
+            mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+        }
     }
 
     private void applyHeight(State state) {
@@ -292,7 +299,8 @@
         apply(mCurrentState);
     }
 
-    public void setRemoteInputActive(boolean remoteInputActive) {
+    @Override
+    public void onRemoteInputActive(boolean remoteInputActive) {
         mCurrentState.remoteInputActive = remoteInputActive;
         apply(mCurrentState);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 101a5f3..4f33d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -23,6 +23,9 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Shader;
@@ -41,6 +44,7 @@
     private float mFramePadding;
     private Bitmap mBitmap;
     private Drawable mDrawable;
+    private boolean mIsDisabled;
 
     private final Paint mFramePaint = new Paint();
     private final Paint mBitmapPaint = new Paint();
@@ -239,4 +243,28 @@
             mDrawable.setState(getDrawableState());
         }
     }
+
+    public void setDisabled(boolean disabled) {
+        if (mIsDisabled == disabled) {
+            return;
+        }
+        mIsDisabled = disabled;
+        int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
+        PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
+                PorterDuff.Mode.SRC_ATOP);
+        if (mBitmap != null) {
+            if (disabled) {
+                mBitmapPaint.setColorFilter(filter);
+            } else {
+                mBitmapPaint.setColorFilter(null);
+            }
+        } else if (mDrawable != null) {
+            if (disabled) {
+                mDrawable.setColorFilter(filter);
+            } else {
+                mDrawable.setColorFilter(null);
+            }
+        }
+        invalidate();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 5071df0..bb3e116 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -70,9 +70,14 @@
         pw.print("  mPowerSave="); pw.println(mPowerSave);
     }
 
+    public void setPowerSaveMode(boolean powerSave) {
+        mPowerManager.setPowerSaveMode(powerSave);
+    }
+
     public void addStateChangedCallback(BatteryStateChangeCallback cb) {
         mChangeCallbacks.add(cb);
         cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+        cb.onPowerSaveChanged(mPowerSave);
     }
 
     public void removeStateChangedCallback(BatteryStateChangeCallback cb) {
@@ -158,12 +163,12 @@
     private void firePowerSaveChanged() {
         final int N = mChangeCallbacks.size();
         for (int i = 0; i < N; i++) {
-            mChangeCallbacks.get(i).onPowerSaveChanged();
+            mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
         }
     }
 
     public interface BatteryStateChangeCallback {
         void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
-        void onPowerSaveChanged();
+        void onPowerSaveChanged(boolean isPowerSave);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 0340984..4ae0321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -35,25 +35,25 @@
 
     private final ScrimView mScrimBehind;
     private final View mBrightnessMirror;
-    private final View mPanelHolder;
+    private final View mNotificationPanel;
     private final int[] mInt2Cache = new int[2];
 
     public BrightnessMirrorController(StatusBarWindowView statusBarWindow) {
         mScrimBehind = (ScrimView) statusBarWindow.findViewById(R.id.scrim_behind);
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
-        mPanelHolder = statusBarWindow.findViewById(R.id.panel_holder);
+        mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
     }
 
     public void showMirror() {
         mBrightnessMirror.setVisibility(View.VISIBLE);
         mScrimBehind.animateViewAlpha(0.0f, TRANSITION_DURATION_OUT, PhoneStatusBar.ALPHA_OUT);
-        outAnimation(mPanelHolder.animate())
+        outAnimation(mNotificationPanel.animate())
                 .withLayer();
     }
 
     public void hideMirror() {
         mScrimBehind.animateViewAlpha(1.0f, TRANSITION_DURATION_IN, PhoneStatusBar.ALPHA_IN);
-        inAnimation(mPanelHolder.animate())
+        inAnimation(mNotificationPanel.animate())
                 .withLayer()
                 .withEndAction(new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index b65bf43..ad8e3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -440,6 +440,7 @@
                         + " dataState=" + state.getDataRegState());
             }
             mServiceState = state;
+            mDataNetType = state.getDataNetworkType();
             updateTelephony();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 65053f3..61a9851 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -26,6 +26,7 @@
 import android.app.RemoteInput;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.Editable;
@@ -174,8 +175,8 @@
     }
 
     public void focus() {
-        mEditText.setInnerFocusable(true);
         mController.addRemoteInput(mEntry);
+        mEditText.setInnerFocusable(true);
         mEditText.mShowImeOnInputConnection = true;
         mEditText.setText(mEntry.remoteInputText);
         mEditText.setSelection(mEditText.getText().length());
@@ -257,6 +258,20 @@
         }
 
         @Override
+        public boolean requestRectangleOnScreen(Rect r) {
+            r.top = mScrollY;
+            r.bottom = mScrollY + (mBottom - mTop);
+            return super.requestRectangleOnScreen(r);
+        }
+
+        @Override
+        public void getFocusedRect(Rect r) {
+            super.getFocusedRect(r);
+            r.top = mScrollY;
+            r.bottom = mScrollY + (mBottom - mTop);
+        }
+
+        @Override
         public boolean onKeyPreIme(int keyCode, KeyEvent event) {
             if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                 defocusIfNeeded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index b010761..05d9626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -22,7 +22,9 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -33,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -48,11 +51,13 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.UserIcons;
+import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.BitmapHelper;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.phone.ActivityStarter;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
 import java.io.FileDescriptor;
@@ -61,6 +66,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 /**
  * Keeps a list of all users on the device for user switching.
  */
@@ -88,6 +95,7 @@
             = new GuestResumeSessionReceiver();
     private final KeyguardMonitor mKeyguardMonitor;
     private final Handler mHandler;
+    private final ActivityStarter mActivityStarter;
 
     private ArrayList<UserRecord> mUsers = new ArrayList<>();
     private Dialog mExitGuestDialog;
@@ -99,11 +107,12 @@
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
 
     public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
-            Handler handler) {
+            Handler handler, ActivityStarter activityStarter) {
         mContext = context;
         mGuestResumeSessionReceiver.register(context);
         mKeyguardMonitor = keyguardMonitor;
         mHandler = handler;
+        mActivityStarter = activityStarter;
         mUserManager = UserManager.get(context);
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_ADDED);
@@ -206,25 +215,23 @@
                     }
                 }
 
-                boolean systemCanCreateUsers = !mUserManager.hasUserRestriction(
-                                UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
                 boolean currentUserCanCreateUsers = currentUserInfo != null
                         && (currentUserInfo.isAdmin()
-                                || currentUserInfo.id == UserHandle.USER_SYSTEM)
-                        && systemCanCreateUsers;
-                boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
-                boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
+                                || currentUserInfo.id == UserHandle.USER_SYSTEM);
+                boolean canCreateGuest = (currentUserCanCreateUsers || addUsersWhenLocked)
                         && guestRecord == null;
-                boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
+                boolean canCreateUser = (currentUserCanCreateUsers || addUsersWhenLocked)
                         && mUserManager.canAddMoreUsers();
                 boolean createIsRestricted = !addUsersWhenLocked;
 
                 if (!mSimpleUserSwitcher) {
                     if (guestRecord == null) {
                         if (canCreateGuest) {
-                            records.add(new UserRecord(null /* info */, null /* picture */,
+                            guestRecord = new UserRecord(null /* info */, null /* picture */,
                                     true /* isGuest */, false /* isCurrent */,
-                                    false /* isAddUser */, createIsRestricted));
+                                    false /* isAddUser */, createIsRestricted);
+                            checkIfAddUserDisallowed(guestRecord);
+                            records.add(guestRecord);
                         }
                     } else {
                         int index = guestRecord.isCurrent ? 0 : records.size();
@@ -233,9 +240,11 @@
                 }
 
                 if (!mSimpleUserSwitcher && canCreateUser) {
-                    records.add(new UserRecord(null /* info */, null /* picture */,
+                    UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
                             false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
-                            createIsRestricted));
+                            createIsRestricted);
+                    checkIfAddUserDisallowed(addUserRecord);
+                    records.add(addUserRecord);
                 }
 
                 return records;
@@ -289,6 +298,14 @@
         return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
     }
 
+    public void logoutCurrentUser() {
+        int currentUser = ActivityManager.getCurrentUser();
+        if (currentUser != UserHandle.USER_SYSTEM) {
+            switchToUserId(UserHandle.USER_SYSTEM);
+            stopUserId(currentUser);
+        }
+    }
+
     public void removeUserId(int userId) {
         if (userId == UserHandle.USER_SYSTEM) {
             Log.w(TAG, "User " + userId + " could not removed.");
@@ -331,6 +348,19 @@
         switchToUserId(id);
     }
 
+    public void switchTo(int userId) {
+        final int count = mUsers.size();
+        for (int i = 0; i < count; ++i) {
+            UserRecord record = mUsers.get(i);
+            if (record.info != null && record.info.id == userId) {
+                switchTo(record);
+                return;
+            }
+        }
+
+        Log.e(TAG, "Couldn't switch to user, id=" + userId);
+    }
+
     private void switchToUserId(int id) {
         try {
             pauseRefreshUsers();
@@ -395,11 +425,7 @@
                 }
                 return;
             } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) {
-                int currentUser = ActivityManager.getCurrentUser();
-                if (currentUser != UserHandle.USER_SYSTEM) {
-                    switchToUserId(UserHandle.USER_SYSTEM);
-                    stopUserId(currentUser);
-                }
+                logoutCurrentUser();
             } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
                 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
                     mExitGuestDialog.cancel();
@@ -594,6 +620,22 @@
         }
     }
 
+    private void checkIfAddUserDisallowed(UserRecord record) {
+        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+                UserManager.DISALLOW_ADD_USER, UserHandle.myUserId());
+        if (admin != null) {
+            record.isDisabledByAdmin = true;
+            record.enforcedAdmin = admin;
+        } else {
+            record.isDisabledByAdmin = false;
+            record.enforcedAdmin = null;
+        }
+    }
+
+    public void startActivity(Intent intent) {
+        mActivityStarter.startActivity(intent, true);
+    }
+
     public static final class UserRecord {
         public final UserInfo info;
         public final Bitmap picture;
@@ -602,6 +644,8 @@
         public final boolean isAddUser;
         /** If true, the record is only visible to the owner and only when unlocked. */
         public final boolean isRestricted;
+        public boolean isDisabledByAdmin;
+        public EnforcedAdmin enforcedAdmin;
 
         public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
                 boolean isAddUser, boolean isRestricted) {
@@ -634,6 +678,10 @@
             if (isCurrent) sb.append(" <isCurrent>");
             if (picture != null) sb.append(" <hasPicture>");
             if (isRestricted) sb.append(" <isRestricted>");
+            if (isDisabledByAdmin) {
+                sb.append(" <isDisabledByAdmin>");
+                sb.append(" enforcedAdmin=" + enforcedAdmin);
+            }
             sb.append(')');
             return sb.toString();
         }
@@ -643,8 +691,8 @@
         private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS");
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_user_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_user_title);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 44b41c5..0406ae3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -33,16 +33,11 @@
 public class TvStatusBar extends BaseStatusBar {
 
     @Override
-    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+    public void setIcon(String slot, StatusBarIcon icon) {
     }
 
     @Override
-    public void updateIcon(String slot, int index, int viewIndex, StatusBarIcon old,
-            StatusBarIcon icon) {
-    }
-
-    @Override
-    public void removeIcon(String slot, int index, int viewIndex) {
+    public void removeIcon(String slot) {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 1e3b0f1..47a4667 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -32,7 +32,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArrayMap;
-
+import android.util.ArraySet;
 import com.android.systemui.BatteryMeterDrawable;
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
@@ -41,9 +41,8 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
+import java.util.Set;
 
 
 public class TunerService extends SystemUI {
@@ -54,7 +53,7 @@
     // Map of Uris we listen on to their settings keys.
     private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
     // Map of settings keys to the listener.
-    private final HashMap<String, List<Tunable>> mTunableLookup = new HashMap<>();
+    private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
 
     private ContentResolver mContentResolver;
     private int mCurrentUser;
@@ -85,7 +84,7 @@
 
     private void addTunable(Tunable tunable, String key) {
         if (!mTunableLookup.containsKey(key)) {
-            mTunableLookup.put(key, new ArrayList<Tunable>());
+            mTunableLookup.put(key, new ArraySet<Tunable>());
         }
         mTunableLookup.get(key).add(tunable);
         Uri uri = Settings.Secure.getUriFor(key);
@@ -99,7 +98,7 @@
     }
 
     public void removeTunable(Tunable tunable) {
-        for (List<Tunable> list : mTunableLookup.values()) {
+        for (Set<Tunable> list : mTunableLookup.values()) {
             list.remove(tunable);
         }
     }
@@ -116,7 +115,7 @@
 
     public void reloadSetting(Uri uri) {
         String key = mListeningUris.get(uri);
-        List<Tunable> tunables = mTunableLookup.get(key);
+        Set<Tunable> tunables = mTunableLookup.get(key);
         if (tunables == null) {
             return;
         }
@@ -153,8 +152,11 @@
     private static TunerService sInstance;
 
     public static TunerService get(Context context) {
-        SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext();
-        TunerService service = sysUi.getComponent(TunerService.class);
+        TunerService service = null;
+        if (context.getApplicationContext() instanceof SystemUIApplication) {
+            SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext();
+            service = sysUi.getComponent(TunerService.class);
+        }
         if (service == null) {
             // Can't get it as a component, must in the tuner, lets just create one for now.
             return getStaticService(context);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
new file mode 100644
index 0000000..2aac69a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -0,0 +1,295 @@
+/*
+ * 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.systemui.tv.pip;
+
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
+import android.app.IActivityManager;
+import android.app.ITaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+
+/**
+ * Manages the picture-in-picture (PIP) UI and states.
+ */
+public class PipManager {
+    private static final String TAG = "PipManager";
+    private static final boolean DEBUG = false;
+
+    private static PipManager sPipManager;
+
+    private static final int STATE_NO_PIP = 0;
+    private static final int STATE_PIP_OVERLAY = 1;
+    private static final int STATE_PIP_MENU = 2;
+
+    private Context mContext;
+    private IActivityManager mActivityManager;
+    private int mState = STATE_NO_PIP;
+    private final Handler mHandler = new Handler();
+    private List<Listener> mListeners = new ArrayList<>();
+    private Rect mPipBound;
+    private Rect mMenuModePipBound;
+    private boolean mInitialized;
+    private final Runnable mOnActivityPinnedRunnable = new Runnable() {
+        @Override
+        public void run() {
+            StackInfo stackInfo = null;
+            try {
+                stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+                if (stackInfo == null) {
+                    Log.w(TAG, "There is no pinned stack");
+                    return;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "getStackInfo failed", e);
+                return;
+            }
+            if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
+            mState = STATE_PIP_OVERLAY;
+            launchPipOverlayActivity();
+        }
+    };
+    private final Runnable mOnTaskStackChanged = new Runnable() {
+        @Override
+        public void run() {
+            if (mState != STATE_NO_PIP) {
+                // TODO: check whether PIP task is closed.
+            }
+        }
+    };
+
+    private final BroadcastReceiver mPipButtonReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Log.d(TAG, "PIP button pressed");
+            if (!hasPipTasks()) {
+                startPip();
+            } else if (mState == STATE_PIP_OVERLAY) {
+                showPipMenu();
+            }
+        }
+    };
+
+    private PipManager() { }
+
+    /**
+     * Initializes {@link PipManager}.
+     */
+    public void initialize(Context context) {
+        if (mInitialized) {
+            return;
+        }
+        mInitialized = true;
+        mContext = context;
+        Resources res = context.getResources();
+        mPipBound = Rect.unflattenFromString(res.getString(
+                com.android.internal.R.string.config_defaultPictureInPictureBounds));
+        mMenuModePipBound = Rect.unflattenFromString(res.getString(
+                com.android.internal.R.string.config_centeredPictureInPictureBounds));
+
+        mActivityManager = ActivityManagerNative.getDefault();
+        TaskStackListener taskStackListener = new TaskStackListener();
+        IActivityManager iam = ActivityManagerNative.getDefault();
+        try {
+            iam.registerTaskStackListener(taskStackListener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "registerTaskStackListener failed", e);
+        }
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PICTURE_IN_PICTURE_BUTTON);
+        mContext.registerReceiver(mPipButtonReceiver, intentFilter);
+    }
+
+    private void startPip() {
+        try {
+            mActivityManager.moveTopActivityToPinnedStack(FULLSCREEN_WORKSPACE_STACK_ID, mPipBound);
+        } catch (RemoteException|IllegalArgumentException e) {
+            Log.e(TAG, "moveTopActivityToPinnedStack failed", e);
+        }
+
+    }
+
+    /**
+     * Closes PIP (PIPped activity and PIP system UI).
+     */
+    public void closePip() {
+        mState = STATE_NO_PIP;
+        StackInfo stackInfo = null;
+        try {
+            stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+            if (stackInfo == null) {
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getStackInfo failed", e);
+            return;
+        }
+        for (int taskId : stackInfo.taskIds) {
+            try {
+                mActivityManager.removeTask(taskId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "removeTask failed", e);
+            }
+        }
+    }
+
+    /**
+     * Moves the PIPped activity to the fullscreen and closes PIP system UI.
+     */
+    public void movePipToFullscreen() {
+        mState = STATE_NO_PIP;
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            mListeners.get(i).onMoveToFullscreen();
+        }
+        try {
+            mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true);
+        } catch (RemoteException e) {
+            Log.e(TAG, "moveTasksToFullscreenStack failed", e);
+        }
+    }
+
+    /**
+     * Shows PIP overlay UI by launching {@link PipOverlayActivity}. It also locates the pinned
+     * stack to the default PIP bound {@link com.android.internal.R.string
+     * .config_defaultPictureInPictureBounds}.
+     */
+    public void showPipOverlay() {
+        if (DEBUG) Log.d(TAG, "showPipOverlay()");
+        try {
+            mActivityManager.resizeStack(PINNED_STACK_ID, mPipBound, false);
+        } catch (Exception e) {
+            Log.e(TAG, "resizeStack failed", e);
+            closePip();
+            return;
+        }
+        mState = STATE_PIP_OVERLAY;
+        launchPipOverlayActivity();
+    }
+
+    /**
+     * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
+     * stack to the centered PIP bound {@link com.android.internal.R.string
+     * .config_centeredPictureInPictureBounds}.
+     */
+    public void showPipMenu() {
+        if (DEBUG) Log.d(TAG, "showPipMenu()");
+        try {
+            mActivityManager.resizeStack(PINNED_STACK_ID, mMenuModePipBound, false);
+        } catch (Exception e) {
+            Log.e(TAG, "resizeStack failed", e);
+            closePip();
+            return;
+        }
+        mState = STATE_PIP_MENU;
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            mListeners.get(i).onShowPipMenu();
+        }
+        Intent intent = new Intent(mContext, PipMenuActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchStackId(PINNED_STACK_ID);
+        mContext.startActivity(intent, options.toBundle());
+    }
+
+    /**
+     * Adds {@link Listener}.
+     */
+    public void addListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes {@link Listener}.
+     */
+    public void removeListener(Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    private void launchPipOverlayActivity() {
+        Intent intent = new Intent(mContext, PipOverlayActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchStackId(PINNED_STACK_ID);
+        mContext.startActivity(intent, options.toBundle());
+    }
+
+    private boolean hasPipTasks() {
+        try {
+            StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+            return stackInfo != null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "getStackInfo failed", e);
+            return false;
+        }
+    }
+
+    private class TaskStackListener extends ITaskStackListener.Stub {
+        @Override
+        public void onTaskStackChanged() throws RemoteException {
+            // Post the message back to the UI thread.
+            mHandler.post(mOnTaskStackChanged);
+        }
+
+        @Override
+        public void onActivityPinned()  throws RemoteException {
+            // Post the message back to the UI thread.
+            mHandler.post(mOnActivityPinnedRunnable);
+        }
+    }
+
+    /**
+     * A listener interface to receive notification on changes in PIP.
+     */
+    public interface Listener {
+        /**
+         * Invoked when a PIPped activity is closed.
+         */
+        void onPipActivityClosed();
+        /**
+         * Invoked when the PIP menu gets shown.
+         */
+        void onShowPipMenu();
+        /**
+         * Invoked when the PIPped activity is returned back to the fullscreen.
+         */
+        void onMoveToFullscreen();
+    }
+
+    /**
+     * Gets an instance of {@link PipManager}.
+     */
+    public static PipManager getInstance() {
+        if (sPipManager == null) {
+            sPipManager = new PipManager();
+        }
+        return sPipManager;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
new file mode 100644
index 0000000..1248321
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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.systemui.tv.pip;
+
+import android.app.Activity;
+import android.media.session.MediaController;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * Activity to show the PIP menu to control PIP.
+ */
+public class PipMenuActivity extends Activity implements PipManager.Listener {
+    private static final String TAG = "PipMenuActivity";
+    private static final boolean DEBUG = false;
+
+    private final PipManager mPipManager = PipManager.getInstance();
+    private MediaController mMediaController;
+
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        setContentView(R.layout.tv_pip_menu);
+        mPipManager.addListener(this);
+        findViewById(R.id.full).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPipManager.movePipToFullscreen();
+            }
+        });
+        findViewById(R.id.exit).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPipManager.closePip();
+                finish();
+            }
+        });
+        findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPipManager.showPipOverlay();
+                finish();
+            }
+        });
+    }
+
+    @Override
+    protected void onDestroy() {
+        mPipManager.removeListener(this);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onBackPressed() {
+        mPipManager.showPipOverlay();
+        finish();
+    }
+
+    @Override
+    public void onPipActivityClosed() {
+        finish();
+    }
+
+    @Override
+    public void onShowPipMenu() { }
+
+    @Override
+    public void onMoveToFullscreen() {
+        finish();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
new file mode 100644
index 0000000..de997a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -0,0 +1,76 @@
+/*
+ * 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.systemui.tv.pip;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * Activity to show an overlay on top of PIP activity to show how to pop up PIP menu.
+ */
+public class PipOverlayActivity extends Activity implements PipManager.Listener {
+    private static final String TAG = "PipOverlayActivity";
+    private static final boolean DEBUG = false;
+
+    private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 2000;
+
+    private final PipManager mPipManager = PipManager.getInstance();
+    private final Handler mHandler = new Handler();
+
+    @Override
+    protected void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        setContentView(R.layout.tv_pip_overlay);
+        mPipManager.addListener(this);
+        final View overlayView = findViewById(R.id.guide_overlay);
+        // TODO: apply animation
+        overlayView.setVisibility(View.VISIBLE);
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                overlayView.setVisibility(View.INVISIBLE);
+            }
+        }, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mHandler.removeCallbacksAndMessages(null);
+        mPipManager.removeListener(this);
+    }
+
+    @Override
+    public void onPipActivityClosed() {
+        finish();
+    }
+
+    @Override
+    public void onShowPipMenu() {
+        finish();
+    }
+
+    @Override
+    public void onMoveToFullscreen() {
+        finish();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
new file mode 100644
index 0000000..182b9b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipUI.java
@@ -0,0 +1,53 @@
+/*
+ * 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.systemui.tv.pip;
+
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+
+import com.android.systemui.SystemUI;
+
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+/**
+ * Controls the picture-in-picture window for TV devices.
+ */
+public class PipUI extends SystemUI {
+    private boolean mSupportPip;
+
+    @Override
+    public void start() {
+        PackageManager pm = mContext.getPackageManager();
+        mSupportPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)
+                && pm.hasSystemFeature(FEATURE_LEANBACK);
+        if (!mSupportPip) {
+            return;
+        }
+        PipManager pipManager = PipManager.getInstance();
+        pipManager.initialize(mContext);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (!mSupportPip) {
+            return;
+        }
+        // TODO: handle configuration change.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 9d4ec10..0901015 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -27,6 +27,7 @@
 import android.app.Dialog;
 import android.app.KeyguardManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
@@ -559,7 +560,13 @@
                 : R.drawable.ic_volume_expand_animation;
         if (res == mExpandButtonRes) return;
         mExpandButtonRes = res;
-        mExpandButton.setImageResource(res);
+        if (hasTouchFeature()) {
+            mExpandButton.setImageResource(res);
+        } else {
+            // if there is no touch feature, show the volume ringer instead
+            mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
+            mExpandButton.setBackgroundResource(0);  // remove gray background emphasis
+        }
         mExpandButton.setContentDescription(mContext.getString(mExpanded ?
                 R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
     }
@@ -648,7 +655,7 @@
         if (D.BUG) Log.d(TAG, "updateFooterH");
         final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE;
         final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF
-                && mAudioManager.isStreamAffectedByRingerMode(mActiveStream);
+                && (mAudioManager.isStreamAffectedByRingerMode(mActiveStream) || mExpanded);
         if (wasVisible != visible && !visible) {
             prepareForCollapse();
         }
@@ -837,6 +844,11 @@
         rescheduleTimeoutH();
     }
 
+    private boolean hasTouchFeature() {
+        final PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
+    }
+
     private final VolumeDialogController.Callbacks mControllerCallbackH
             = new VolumeDialogController.Callbacks() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index cd47ac1..38d8de0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -510,7 +510,7 @@
         GregorianCalendar weekRange = new GregorianCalendar();
         final long now = weekRange.getTimeInMillis();
         setToMidnight(weekRange);
-        weekRange.roll(Calendar.DATE, 6);
+        weekRange.add(Calendar.DATE, 6);
         final long nextAlarmMs = mController.getNextAlarm();
         if (nextAlarmMs > 0) {
             GregorianCalendar nextAlarm = new GregorianCalendar();
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c21af24..2825601 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -19,10 +19,16 @@
 
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
         <activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" />
+
+        <service
+            android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService"
+            android:process=":killable" />
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
new file mode 100644
index 0000000..f86c6a4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.external;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSService;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.test.AndroidTestCase;
+import android.util.ArraySet;
+import android.util.Log;
+
+public class TileLifecycleManagerTests extends AndroidTestCase {
+    public static final String TILE_UPDATE_BROADCAST = "com.android.systemui.tests.TILE_UPDATE";
+    public static final String EXTRA_CALLBACK = "callback";
+
+    private HandlerThread mThread;
+    private Handler mHandler;
+    private TileLifecycleManager mStateManager;
+    private final Object mBroadcastLock = new Object();
+    private final ArraySet<String> mCallbacks = new ArraySet<>();
+    private boolean mBound;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mThread = new HandlerThread("TestThread");
+        mThread.start();
+        mHandler = new Handler(mThread.getLooper());
+        mStateManager = new TileLifecycleManager(mHandler, getContext(),
+                new Intent(mContext, FakeTileService.class), new UserHandle(UserHandle.myUserId()));
+        mCallbacks.clear();
+        getContext().registerReceiver(mReceiver, new IntentFilter(TILE_UPDATE_BROADCAST));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (mBound) {
+            unbindService();
+        }
+        mThread.quit();
+        getContext().unregisterReceiver(mReceiver);
+    }
+
+    public void testSync() {
+        syncWithHandler();
+    }
+
+    public void testBind() {
+        bindService();
+        waitForCallback("onCreate");
+    }
+
+    public void testUnbind() {
+        bindService();
+        waitForCallback("onCreate");
+        unbindService();
+        waitForCallback("onDestroy");
+    }
+
+    public void testTileServiceCallbacks() {
+        bindService();
+        waitForCallback("onCreate");
+
+        mStateManager.onTileAdded();
+        waitForCallback("onTileAdded");
+        mStateManager.onStartListening();
+        waitForCallback("onStartListening");
+        mStateManager.onClick(null);
+        waitForCallback("onClick");
+        mStateManager.onStopListening();
+        waitForCallback("onStopListening");
+        mStateManager.onTileRemoved();
+        waitForCallback("onTileRemoved");
+
+        unbindService();
+    }
+
+    public void testAddedBeforeBind() {
+        mStateManager.onTileAdded();
+
+        bindService();
+        waitForCallback("onCreate");
+        waitForCallback("onTileAdded");
+    }
+
+    public void testListeningBeforeBind() {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+
+        bindService();
+        waitForCallback("onCreate");
+        waitForCallback("onTileAdded");
+        waitForCallback("onStartListening");
+    }
+
+    public void testClickBeforeBind() {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+        mStateManager.onClick(null);
+
+        bindService();
+        waitForCallback("onCreate");
+        waitForCallback("onTileAdded");
+        waitForCallback("onStartListening");
+        waitForCallback("onClick");
+    }
+
+    public void testListeningNotListeningBeforeBind() {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+        mStateManager.onStopListening();
+
+        bindService();
+        waitForCallback("onCreate");
+        unbindService();
+        waitForCallback("onDestroy");
+        assertFalse(mCallbacks.contains("onStartListening"));
+    }
+
+    public void testNoClickOfNotListeningAnymore() {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+        mStateManager.onClick(null);
+        mStateManager.onStopListening();
+
+        bindService();
+        waitForCallback("onCreate");
+        unbindService();
+        waitForCallback("onDestroy");
+        assertFalse(mCallbacks.contains("onClick"));
+    }
+
+    public void testComponentEnabling() {
+        mStateManager.onTileAdded();
+        mStateManager.onStartListening();
+
+        PackageManager pm = getContext().getPackageManager();
+        pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+
+        bindService();
+        assertTrue(mStateManager.mReceiverRegistered);
+
+        pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+        waitForCallback("onCreate");
+    }
+
+    public void testKillProcess() {
+        mStateManager.onStartListening();
+        bindService();
+        waitForCallback("onCreate");
+        waitForCallback("onStartListening");
+
+        getContext().sendBroadcast(new Intent(FakeTileService.ACTION_KILL));
+
+        waitForCallback("onCreate");
+        waitForCallback("onStartListening");
+    }
+
+    private void bindService() {
+        mBound = true;
+        mStateManager.setBindService(true);
+    }
+
+    private void unbindService() {
+        mBound = false;
+        mStateManager.setBindService(false);
+    }
+
+    private void waitForCallback(String callback) {
+        for (int i = 0; i < 25; i++) {
+            if (mCallbacks.contains(callback)) {
+                mCallbacks.remove(callback);
+                return;
+            }
+            synchronized (mBroadcastLock) {
+                try {
+                    mBroadcastLock.wait(500);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        if (mCallbacks.contains(callback)) {
+            mCallbacks.remove(callback);
+            return;
+        }
+        fail("Didn't receive callback: " + callback);
+    }
+
+    private void syncWithHandler() {
+        final Object lock = new Object();
+        synchronized (lock) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (lock) {
+                        lock.notify();
+                    }
+                }
+            });
+            try {
+                lock.wait(5000);
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mCallbacks.add(intent.getStringExtra(EXTRA_CALLBACK));
+            synchronized (mBroadcastLock) {
+                mBroadcastLock.notify();
+            }
+        }
+    };
+
+    public static class FakeTileService extends Service {
+        public static final String ACTION_KILL = "com.android.systemui.test.KILL";
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            return new IQSTileService.Stub() {
+
+                @Override
+                public void setQSService(IQSService service) {
+
+                }
+
+                @Override
+                public void setQSTile(Tile tile) throws RemoteException {
+                }
+
+                @Override
+                public void onTileAdded() throws RemoteException {
+                    sendCallback("onTileAdded");
+                }
+
+                @Override
+                public void onTileRemoved() throws RemoteException {
+                    sendCallback("onTileRemoved");
+                }
+
+                @Override
+                public void onStartListening() throws RemoteException {
+                    sendCallback("onStartListening");
+                }
+
+                @Override
+                public void onStopListening() throws RemoteException {
+                    sendCallback("onStopListening");
+                }
+
+                @Override
+                public void onClick(IBinder iBinder) throws RemoteException {
+                    sendCallback("onClick");
+                }
+
+                @Override
+                public void onUnlockComplete() throws RemoteException {
+                    sendCallback("onUnlockComplete");
+                }
+            };
+        }
+
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            registerReceiver(mReceiver, new IntentFilter(ACTION_KILL));
+            sendCallback("onCreate");
+        }
+
+        @Override
+        public void onDestroy() {
+            super.onDestroy();
+            unregisterReceiver(mReceiver);
+            sendCallback("onDestroy");
+        }
+
+        private void sendCallback(String callback) {
+            Log.d("TileLifecycleManager", "Relaying: " + callback);
+            sendBroadcast(new Intent(TILE_UPDATE_BROADCAST)
+                    .putExtra(EXTRA_CALLBACK, callback));
+        }
+
+        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (ACTION_KILL.equals(intent.getAction())) {
+                    Process.killProcess(Process.myPid());
+                }
+            }
+        };
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
new file mode 100644
index 0000000..4586c28
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.service.quicksettings.TileService;
+import com.android.systemui.SysuiTestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+public class TileServiceManagerTests extends SysuiTestCase {
+
+    private TileServices mTileServices;
+    private TileLifecycleManager mTileLifecycle;
+    private HandlerThread mThread;
+    private Handler mHandler;
+    private TileServiceManager mTileServiceManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mThread = new HandlerThread("TestThread");
+        mThread.start();
+        mHandler = new Handler(mThread.getLooper());
+        mTileServices = Mockito.mock(TileServices.class);
+        Mockito.when(mTileServices.getContext()).thenReturn(mContext);
+        mTileLifecycle = Mockito.mock(TileLifecycleManager.class);
+        ComponentName componentName = new ComponentName(mContext,
+                TileServiceManagerTests.class);
+        Mockito.when(mTileLifecycle.getComponent()).thenReturn(componentName);
+        mContext.getSharedPreferences(TileServiceManager.PREFS_FILE, 0).edit()
+                .putInt(componentName.flattenToString(), TileService.TILE_MODE_PASSIVE).commit();
+        mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mTileLifecycle);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mThread.quit();
+    }
+
+    public void testSetBindRequested() {
+        // Request binding.
+        mTileServiceManager.setBindRequested(true);
+        mTileServiceManager.setLastUpdate(0);
+        mTileServiceManager.calculateBindPriority(5);
+        Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance();
+        assertEquals(5, mTileServiceManager.getBindPriority());
+
+        // Verify same state doesn't trigger recalculating for no reason.
+        mTileServiceManager.setBindRequested(true);
+        Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance();
+
+        mTileServiceManager.setBindRequested(false);
+        mTileServiceManager.calculateBindPriority(5);
+        Mockito.verify(mTileServices, Mockito.times(3)).recalculateBindAllowance();
+        assertEquals(Integer.MIN_VALUE, mTileServiceManager.getBindPriority());
+    }
+
+    public void testPendingClickPriority() {
+        Mockito.when(mTileLifecycle.hasPendingClick()).thenReturn(true);
+        mTileServiceManager.calculateBindPriority(0);
+        assertEquals(Integer.MAX_VALUE, mTileServiceManager.getBindPriority());
+    }
+
+    public void testBind() {
+        // Trigger binding requested and allowed.
+        mTileServiceManager.setBindRequested(true);
+        mTileServiceManager.setBindAllowed(true);
+
+        ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+        Mockito.verify(mTileLifecycle, Mockito.times(1)).setBindService(captor.capture());
+        assertTrue((boolean) captor.getValue());
+
+        mTileServiceManager.setBindRequested(false);
+        mTileServiceManager.calculateBindPriority(0);
+        // Priority shouldn't disappear after the request goes away if we just bound, instead
+        // it sticks around to avoid thrashing a bunch of processes.
+        assertEquals(Integer.MAX_VALUE - 1, mTileServiceManager.getBindPriority());
+
+        mTileServiceManager.setBindAllowed(false);
+        captor = ArgumentCaptor.forClass(Boolean.class);
+        Mockito.verify(mTileLifecycle, Mockito.times(2)).setBindService(captor.capture());
+        assertFalse((boolean) captor.getValue());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
new file mode 100644
index 0000000..c4ca039
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.content.ComponentName;
+import android.os.Looper;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+
+public class TileServicesTests extends SysuiTestCase {
+    private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
+
+    private TileServices mTileService;
+    private ArrayList<TileServiceManager> mManagers;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mManagers = new ArrayList<>();
+        QSTileHost host = new QSTileHost(mContext, null, null, null, null, null, null, null, null,
+                null, null, null, null, null, null, null);
+        mTileService = new TestTileServices(host, Looper.myLooper());
+    }
+
+    public void testRecalculateBindAllowance() {
+        // Add some fake tiles.
+        for (int i = 0; i < NUM_FAKES; i++) {
+            mTileService.getTileWrapper(Mockito.mock(CustomTile.class));
+        }
+        assertEquals(NUM_FAKES, mManagers.size());
+
+        for (int i = 0; i < NUM_FAKES; i++) {
+            Mockito.when(mManagers.get(i).getBindPriority()).thenReturn(i);
+        }
+        mTileService.recalculateBindAllowance();
+        for (int i = 0; i < NUM_FAKES; i++) {
+            Mockito.verify(mManagers.get(i), Mockito.times(1)).calculateBindPriority(
+                    Mockito.anyLong());
+            ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+            Mockito.verify(mManagers.get(i), Mockito.times(1)).setBindAllowed(captor.capture());
+
+            assertEquals("" + i + "th service", i >= (NUM_FAKES - TileServices.DEFAULT_MAX_BOUND),
+                    (boolean) captor.getValue());
+        }
+    }
+
+    public void testSetMemoryPressure() {
+        testRecalculateBindAllowance();
+        mTileService.setMemoryPressure(true);
+
+        for (int i = 0; i < NUM_FAKES; i++) {
+            ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+            Mockito.verify(mManagers.get(i), Mockito.times(2)).setBindAllowed(captor.capture());
+
+            assertEquals("" + i + "th service", i >= (NUM_FAKES - TileServices.REDUCED_MAX_BOUND),
+                    (boolean) captor.getValue());
+        }
+    }
+
+    public void testCalcFew() {
+        for (int i = 0; i < TileServices.DEFAULT_MAX_BOUND - 1; i++) {
+            mTileService.getTileWrapper(Mockito.mock(CustomTile.class));
+        }
+        mTileService.recalculateBindAllowance();
+
+        for (int i = 0; i < TileServices.DEFAULT_MAX_BOUND - 1; i++) {
+            // Shouldn't get bind prioirities calculated when there are less than the max services.
+            Mockito.verify(mManagers.get(i), Mockito.never()).calculateBindPriority(
+                    Mockito.anyLong());
+
+            // All should be bound since there are less than the max services.
+            ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+            Mockito.verify(mManagers.get(i), Mockito.times(1)).setBindAllowed(captor.capture());
+
+            assertTrue(captor.getValue());
+        }
+    }
+
+    private class TestTileServices extends TileServices {
+        public TestTileServices(QSTileHost host, Looper looper) {
+            super(host, looper);
+        }
+
+        @Override
+        protected TileServiceManager onCreateTileService(ComponentName component) {
+            TileServiceManager manager = Mockito.mock(TileServiceManager.class);
+            mManagers.add(manager);
+            return manager;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 13fc47d..b64fbea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -60,8 +60,8 @@
     protected NetworkControllerImpl mNetworkController;
     protected MobileSignalController mMobileSignalController;
     protected PhoneStateListener mPhoneStateListener;
-    private SignalStrength mSignalStrength;
-    private ServiceState mServiceState;
+    protected SignalStrength mSignalStrength;
+    protected ServiceState mServiceState;
     protected ConnectivityManager mMockCm;
     protected WifiManager mMockWm;
     protected SubscriptionManager mMockSm;
@@ -235,7 +235,7 @@
         mPhoneStateListener.onSignalStrengthsChanged(mSignalStrength);
     }
 
-    private void updateServiceState() {
+    protected void updateServiceState() {
         Log.d(TAG, "Sending Service State: " + mServiceState);
         mPhoneStateListener.onServiceStateChanged(mServiceState);
     }
@@ -246,6 +246,7 @@
     }
 
     public void updateDataConnectionState(int dataState, int dataNetType) {
+        when(mServiceState.getDataNetworkType()).thenReturn(dataNetType);
         mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 587e2b5..e4f858b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -115,6 +115,21 @@
                 TelephonyIcons.QS_DATA_4G);
     }
 
+    public void testDataChangeWithoutConnectionState() {
+        setupDefaultSignal();
+        updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+                TelephonyManager.NETWORK_TYPE_LTE);
+
+        verifyDataIndicators(TelephonyIcons.DATA_LTE[1][0 /* No direction */],
+                TelephonyIcons.QS_DATA_LTE);
+
+        Mockito.when(mServiceState.getDataNetworkType())
+                .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
+        updateServiceState();
+        verifyDataIndicators(TelephonyIcons.DATA_H[1][0 /* No direction */],
+                TelephonyIcons.QS_DATA_H);
+    }
+
     public void testDataActivity() {
         setupDefaultSignal();
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 28121b4..e310801 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -64,6 +64,14 @@
     // Indicates that motion events are being collected to match a gesture.
     private boolean mRecognizingGesture;
 
+    // Indicates that motion events from the second pointer are being checked
+    // for a double tap.
+    private boolean mSecondFingerDoubleTap;
+
+    // Tracks the most recent time where ACTION_POINTER_DOWN was sent for the
+    // second pointer.
+    private long mSecondPointerDownTime;
+
     // Policy flags of the previous event.
     private int mPolicyFlags;
 
@@ -102,6 +110,7 @@
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mDoubleTapDetected = false;
+                mSecondFingerDoubleTap = false;
                 mRecognizingGesture = true;
                 mPreviousX = x;
                 mPreviousY = y;
@@ -133,6 +142,43 @@
                     }
                 }
                 break;
+
+            case MotionEvent.ACTION_POINTER_DOWN:
+                // Once a second finger is used, we're definitely not
+                // recognizing a gesture.
+                cancelGesture();
+
+                if (event.getPointerCount() == 2) {
+                    // If this was the second finger, attempt to recognize double
+                    // taps on it.
+                    mSecondFingerDoubleTap = true;
+                    mSecondPointerDownTime = event.getEventTime();
+                } else {
+                    // If there are more than two fingers down, stop watching
+                    // for a double tap.
+                    mSecondFingerDoubleTap = false;
+                }
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                // If we're detecting taps on the second finger, see if we
+                // should finish the double tap.
+                if (mSecondFingerDoubleTap && maybeFinishDoubleTap(event, policyFlags)) {
+                    return true;
+                }
+                break;
+        }
+
+        // If we're detecting taps on the second finger, map events from the
+        // finger to the first finger.
+        if (mSecondFingerDoubleTap) {
+            MotionEvent newEvent = mapSecondPointerToFirstPointer(event);
+            if (newEvent == null) {
+                return false;
+            }
+            boolean handled = mGestureDetector.onTouchEvent(newEvent);
+            newEvent.recycle();
+            return handled;
         }
 
         if (!mRecognizingGesture) {
@@ -146,6 +192,7 @@
     public void clear() {
         mFirstTapDetected = false;
         mDoubleTapDetected = false;
+        mSecondFingerDoubleTap = false;
         cancelGesture();
         mStrokeBuffer.clear();
     }
@@ -229,4 +276,28 @@
 
         return false;
     }
+
+    private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) {
+        // Only map basic events when two fingers are down.
+        if (event.getPointerCount() != 2 ||
+                (event.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN &&
+                 event.getActionMasked() != MotionEvent.ACTION_POINTER_UP &&
+                 event.getActionMasked() != MotionEvent.ACTION_MOVE)) {
+            return null;
+        }
+
+        int action = event.getActionMasked();
+
+        if (action == MotionEvent.ACTION_POINTER_DOWN) {
+            action = MotionEvent.ACTION_DOWN;
+        } else if (action == MotionEvent.ACTION_POINTER_UP) {
+            action = MotionEvent.ACTION_UP;
+        }
+
+        // Map the information from the second pointer to the first.
+        return MotionEvent.obtain(mSecondPointerDownTime, event.getEventTime(), action,
+                event.getX(1), event.getY(1), event.getPressure(1), event.getSize(1),
+                event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
+                event.getDeviceId(), event.getEdgeFlags());
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 5f6cbf9..232c080 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -69,6 +69,13 @@
      */
     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
 
+    /**
+     * Flag for enabling motion event injectsion
+     *
+     * @see #setUserAndEnabledFeatures(int, int)
+     */
+    static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
+
     private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
         @Override
         public void run() {
@@ -104,6 +111,8 @@
 
     private MagnificationGestureHandler mMagnificationGestureHandler;
 
+    private MotionEventInjector mMotionEventInjector;
+
     private AutoclickController mAutoclickController;
 
     private KeyboardInterceptor mKeyboardInterceptor;
@@ -191,6 +200,10 @@
         }
     }
 
+    public MotionEventInjector getMotionEventInjector() {
+        return mMotionEventInjector;
+    }
+
     /**
      * Gets current event stream state associated with an input event.
      * @return The event stream state that should be used for the event. Null if the event should
@@ -351,6 +364,12 @@
     private void enableFeatures() {
         resetStreamState();
 
+        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+            mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper());
+            addFirstEventHandler(mMotionEventInjector);
+            mAms.setMotionEventInjector(mMotionEventInjector);
+        }
+
         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
             mAutoclickController = new AutoclickController(mContext, mUserId);
             addFirstEventHandler(mAutoclickController);
@@ -388,6 +407,11 @@
     }
 
     private void disableFeatures() {
+        if (mMotionEventInjector != null) {
+            mAms.setMotionEventInjector(null);
+            mMotionEventInjector.onDestroy();
+            mMotionEventInjector = null;
+        }
         if (mAutoclickController != null) {
             mAutoclickController.onDestroy();
             mAutoclickController = null;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8cf25b3..28aeef7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -37,6 +37,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
@@ -72,6 +73,7 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
+import android.view.MotionEvent;
 import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.WindowManagerInternal;
@@ -186,6 +188,8 @@
 
     private KeyEventDispatcher mKeyEventDispatcher;
 
+    private MotionEventInjector mMotionEventInjector;
+
     private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();
 
     private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
@@ -711,7 +715,7 @@
     }
 
     @Override
-    public IBinder getWindowToken(int windowId) {
+    public IBinder getWindowToken(int windowId, int userId) {
         mSecurityPolicy.enforceCallingPermission(
                 Manifest.permission.RETRIEVE_WINDOW_TOKEN,
                 GET_WINDOW_TOKEN);
@@ -720,8 +724,7 @@
             // share the accessibility state of the parent. The call below
             // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
-                    .resolveCallingUserIdEnforcingPermissionsLocked(
-                            UserHandle.getCallingUserId());
+                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             if (resolvedUserId != mCurrentUserId) {
                 return null;
             }
@@ -779,6 +782,18 @@
     }
 
     /**
+     * Called by AccessibilityInputFilter when it creates or destroys the motionEventInjector.
+     * Not using a getter because the AccessibilityInputFilter isn't thread-safe
+     *
+     * @param motionEventInjector The new value of the motionEventInjector. May be null.
+     */
+    void setMotionEventInjector(MotionEventInjector motionEventInjector) {
+        synchronized (mLock) {
+            mMotionEventInjector = motionEventInjector;
+        }
+    }
+
+    /**
      * Gets a point within the accessibility focused node where we can send down
      * and up events to perform a click.
      *
@@ -1273,6 +1288,9 @@
             if (userState.mIsAutoclickEnabled) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
             }
+            if (userState.mIsPerformGesturesEnabled) {
+                flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
+            }
             if (flags != 0) {
                 if (!mHasInputFilter) {
                     mHasInputFilter = true;
@@ -1363,6 +1381,7 @@
         updateAccessibilityFocusBehaviorLocked(userState);
         updateFilterKeyEventsLocked(userState);
         updateTouchExplorationLocked(userState);
+        updatePerformGesturesLocked(userState);
         updateEnhancedWebAccessibilityLocked(userState);
         updateDisplayColorAdjustmentSettingsLocked(userState);
         updateMagnificationLocked(userState);
@@ -1451,6 +1470,19 @@
         }
     }
 
+    private void updatePerformGesturesLocked(UserState userState) {
+        final int serviceCount = userState.mBoundServices.size();
+        for (int i = 0; i < serviceCount; i++) {
+            Service service = userState.mBoundServices.get(i);
+            if ((service.mAccessibilityServiceInfo.getCapabilities()
+                    & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
+                userState.mIsPerformGesturesEnabled = true;
+                return;
+            }
+        }
+        userState.mIsPerformGesturesEnabled = false;
+    }
+
     private void updateFilterKeyEventsLocked(UserState userState) {
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
@@ -2564,6 +2596,23 @@
         }
 
         @Override
+        public void sendMotionEvents(int sequence, ParceledListSlice events) {
+            synchronized (mLock) {
+                if (mSecurityPolicy.canPerformGestures(this) && (mMotionEventInjector != null)) {
+                    mMotionEventInjector.injectEvents((List<MotionEvent>) events.getList(),
+                            mServiceInterface, sequence);
+                    return;
+                }
+            }
+            try {
+                mServiceInterface.onPerformGestureResult(sequence, false);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending motion event injection failure to "
+                        + mServiceInterface, re);
+            }
+        }
+
+        @Override
         public boolean performAccessibilityAction(int accessibilityWindowId,
                 long accessibilityNodeId, int action, Bundle arguments, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -3734,6 +3783,11 @@
                     & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
         }
 
+        public boolean canPerformGestures(Service service) {
+            return (service.mAccessibilityServiceInfo.getCapabilities()
+                    & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
+        }
+
         private int resolveProfileParentLocked(int userId) {
             if (userId != mCurrentUserId) {
                 final long identity = Binder.clearCallingIdentity();
@@ -3878,6 +3932,7 @@
         public boolean mIsEnhancedWebAccessibilityEnabled;
         public boolean mIsDisplayMagnificationEnabled;
         public boolean mIsAutoclickEnabled;
+        public boolean mIsPerformGesturesEnabled;
         public boolean mIsFilterKeyEventsEnabled;
         public boolean mHasDisplayColorAdjustment;
         public boolean mAccessibilityFocusOnlyInActiveWindow;
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
new file mode 100644
index 0000000..800f0e1
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceClient;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.os.SomeArgs;
+import com.android.server.accessibility.AccessibilityManagerService.Service;
+
+import java.util.List;
+
+/**
+ * Injects MotionEvents to permit {@code AccessibilityService}s to touch the screen on behalf of
+ * users.
+ *
+ * All methods except {@code injectEvents} must be called only from the main thread.
+ */
+public class MotionEventInjector implements EventStreamTransformation {
+    private static final String LOG_TAG = "MotionEventInjector";
+    private static final int MESSAGE_SEND_MOTION_EVENT = 1;
+    private static final int MESSAGE_INJECT_EVENTS = 2;
+    private static final int MAX_POINTERS = 11; // Non-binding maximum
+
+    private final Handler mHandler;
+    private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
+
+    // These two arrays must be the same length
+    private MotionEvent.PointerProperties[] mPointerProperties =
+            new MotionEvent.PointerProperties[MAX_POINTERS];
+    private MotionEvent.PointerCoords[] mPointerCoords =
+            new MotionEvent.PointerCoords[MAX_POINTERS];
+    private EventStreamTransformation mNext;
+    private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
+    private int mSequenceForCurrentGesture;
+    private int mSourceOfInjectedGesture = InputDevice.SOURCE_UNKNOWN;
+    private boolean mIsDestroyed = false;
+
+    /**
+     * @param looper A looper on the main thread to use for dispatching new events
+     */
+    public MotionEventInjector(Looper looper) {
+        mHandler = new Handler(looper, new Callback());
+    }
+
+    /**
+     * Schedule a series of events for injection. These events must comprise a complete, valid
+     * sequence. All gestures currently in progress will be cancelled, and all {@code downTime}
+     * and {@code eventTime} fields will be offset by the current time.
+     *
+     * @param events The events to inject. Must all be from the same source.
+     * @param serviceInterface The interface to call back with a result when the gesture is
+     * either complete or cancelled.
+     */
+    public void injectEvents(List<MotionEvent> events,
+            IAccessibilityServiceClient serviceInterface, int sequence) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = events;
+        args.arg2 = serviceInterface;
+        args.argi1 = sequence;
+        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_INJECT_EVENTS, args));
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAnyPendingInjectedEvents();
+        sendMotionEventToNext(event, rawEvent, policyFlags);
+    }
+
+    @Override
+    public void onKeyEvent(KeyEvent event, int policyFlags) {
+        if (mNext != null) {
+            mNext.onKeyEvent(event, policyFlags);
+        }
+    }
+
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {
+        if (mNext != null) {
+            mNext.onAccessibilityEvent(event);
+        }
+    }
+
+    @Override
+    public void setNext(EventStreamTransformation next) {
+        mNext = next;
+    }
+
+    @Override
+    public void clearEvents(int inputSource) {
+        /*
+         * Reset state for motion events passing through so we won't send a cancel event for
+         * them.
+         */
+        if (!mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) {
+            mOpenGesturesInProgress.put(inputSource, false);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        cancelAnyPendingInjectedEvents();
+        mIsDestroyed = true;
+    }
+
+    private void injectEventsMainThread(List<MotionEvent> events,
+            IAccessibilityServiceClient serviceInterface, int sequence) {
+        if (mIsDestroyed) {
+            try {
+                serviceInterface.onPerformGestureResult(sequence, false);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error sending status with mIsDestroyed to " + serviceInterface,
+                        re);
+            }
+            return;
+        }
+        cancelAnyPendingInjectedEvents();
+        mSourceOfInjectedGesture = events.get(0).getSource();
+        cancelAnyGestureInProgress(mSourceOfInjectedGesture);
+        mServiceInterfaceForCurrentGesture = serviceInterface;
+        mSequenceForCurrentGesture = sequence;
+        if (mNext == null) {
+            notifyService(false);
+            return;
+        }
+
+        long startTime = SystemClock.uptimeMillis();
+        for (int i = 0; i < events.size(); i++) {
+            MotionEvent event = events.get(i);
+            int numPointers = event.getPointerCount();
+            if (numPointers > mPointerCoords.length) {
+                mPointerCoords = new MotionEvent.PointerCoords[numPointers];
+                mPointerProperties = new MotionEvent.PointerProperties[numPointers];
+            }
+            for (int j = 0; j < numPointers; j++) {
+                if (mPointerCoords[j] == null) {
+                    mPointerCoords[j] = new MotionEvent.PointerCoords();
+                    mPointerProperties[j] = new MotionEvent.PointerProperties();
+                }
+                event.getPointerCoords(j, mPointerCoords[j]);
+                event.getPointerProperties(j, mPointerProperties[j]);
+            }
+
+            /*
+             * MotionEvent doesn't have a setEventTime() method (it carries around history data,
+             * which could become inconsistent), so we need to obtain a new one.
+             */
+            MotionEvent offsetEvent = MotionEvent.obtain(startTime + event.getDownTime(),
+                    startTime + event.getEventTime(), event.getAction(), numPointers,
+                    mPointerProperties, mPointerCoords, event.getMetaState(),
+                    event.getButtonState(), event.getXPrecision(), event.getYPrecision(),
+                    event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
+                    event.getFlags());
+            Message message = mHandler.obtainMessage(MESSAGE_SEND_MOTION_EVENT, offsetEvent);
+            mHandler.sendMessageDelayed(message, event.getEventTime());
+        }
+    }
+
+    private void sendMotionEventToNext(MotionEvent event, MotionEvent rawEvent,
+            int policyFlags) {
+        if (mNext != null) {
+            mNext.onMotionEvent(event, rawEvent, policyFlags);
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mOpenGesturesInProgress.put(event.getSource(), true);
+            }
+            if ((event.getActionMasked() == MotionEvent.ACTION_UP)
+                    || (event.getActionMasked() == MotionEvent.ACTION_CANCEL)) {
+                mOpenGesturesInProgress.put(event.getSource(), false);
+            }
+        }
+    }
+
+    private void cancelAnyGestureInProgress(int source) {
+        if ((mNext != null) && mOpenGesturesInProgress.get(source, false)) {
+            long now = SystemClock.uptimeMillis();
+            MotionEvent cancelEvent =
+                    MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+            sendMotionEventToNext(cancelEvent, cancelEvent,
+                    WindowManagerPolicy.FLAG_PASS_TO_USER);
+        }
+    }
+
+    private void cancelAnyPendingInjectedEvents() {
+        if (mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) {
+            cancelAnyGestureInProgress(mSourceOfInjectedGesture);
+            mHandler.removeMessages(MESSAGE_SEND_MOTION_EVENT);
+            notifyService(false);
+        }
+
+    }
+
+    private void notifyService(boolean success) {
+        try {
+            mServiceInterfaceForCurrentGesture.onPerformGestureResult(
+                    mSequenceForCurrentGesture, success);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error sending motion event injection status to "
+                    + mServiceInterfaceForCurrentGesture, re);
+        }
+    }
+
+    private class Callback implements Handler.Callback {
+        @Override
+        public boolean handleMessage(Message message) {
+            if (message.what == MESSAGE_INJECT_EVENTS) {
+                SomeArgs args = (SomeArgs) message.obj;
+                injectEventsMainThread((List<MotionEvent>) args.arg1,
+                        (IAccessibilityServiceClient) args.arg2, args.argi1);
+                args.recycle();
+                return true;
+            }
+            if (message.what != MESSAGE_SEND_MOTION_EVENT) {
+                throw new IllegalArgumentException("Unknown message: " + message.what);
+            }
+            MotionEvent motionEvent = (MotionEvent) message.obj;
+            sendMotionEventToNext(motionEvent, motionEvent,
+                    WindowManagerPolicy.FLAG_PASS_TO_USER);
+            // If the message queue is now empty, then this gesture is complete
+            if (!mHandler.hasMessages(MESSAGE_SEND_MOTION_EVENT)) {
+                notifyService(true);
+            }
+            return true;
+        }
+    }
+}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6f8f8eb..db901aa 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.server.appwidget;
 
-import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -2206,9 +2205,11 @@
             final Resources resources;
             final long identity = Binder.clearCallingIdentity();
             try {
-                resources = mContext.getPackageManager()
-                        .getResourcesForApplicationAsUser(activityInfo.packageName,
-                                UserHandle.getUserId(providerId.uid));
+                final PackageManager pm = mContext.getPackageManager();
+                final int userId = UserHandle.getUserId(providerId.uid);
+                final ApplicationInfo app = pm.getApplicationInfoAsUser(activityInfo.packageName,
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+                resources = pm.getResourcesForApplication(app);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2309,6 +2310,10 @@
         try {
             int flags = PackageManager.GET_META_DATA;
 
+            // We really need packages to be around and parsed to know if they
+            // provide widgets, and we only load widgets after user is unlocked.
+            flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+
             // Widgets referencing shared libraries need to have their
             // dependencies loaded.
             flags |= PackageManager.GET_SHARED_LIBRARY_FILES;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2264c69..aa15373 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -239,6 +239,11 @@
     // How long between attempts to perform a full-data backup of any given app
     static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
 
+    // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+    // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+    static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
+    static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
+
     Context mContext;
     private PackageManager mPackageManager;
     IPackageManager mPackageManagerBinder;
@@ -4496,35 +4501,72 @@
                 return false;
             }
 
-            // At this point we know that we have work to do, just not right now.  Any
-            // exit without actually running backups will also require that we
+            // At this point we know that we have work to do, but possibly not right now.
+            // Any exit without actually running backups will also require that we
             // reschedule the job.
             boolean runBackup = true;
+            boolean headBusy;
 
-            if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "Preconditions not met; not running full backup");
-                }
-                runBackup = false;
-                // Typically this means we haven't run a key/value backup yet.  Back off
-                // full-backup operations by the key/value job's run interval so that
-                // next time we run, we are likely to be able to make progress.
-                latency = KeyValueBackupJob.BATCH_INTERVAL;
-            }
+            do {
+                headBusy = false;
 
-            if (runBackup) {
-                entry = mFullBackupQueue.get(0);
-                long timeSinceRun = now - entry.lastBackup;
-                runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
-                if (!runBackup) {
-                    // It's too early to back up the next thing in the queue, so bow out
+                if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
                     if (MORE_DEBUG) {
-                        Slog.i(TAG, "Device ready but too early to back up next app");
+                        Slog.i(TAG, "Preconditions not met; not running full backup");
                     }
-                    // Wait until the next app in the queue falls due for a full data backup
-                    latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                    runBackup = false;
+                    // Typically this means we haven't run a key/value backup yet.  Back off
+                    // full-backup operations by the key/value job's run interval so that
+                    // next time we run, we are likely to be able to make progress.
+                    latency = KeyValueBackupJob.BATCH_INTERVAL;
                 }
-            }
+
+                if (runBackup) {
+                    entry = mFullBackupQueue.get(0);
+                    long timeSinceRun = now - entry.lastBackup;
+                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+                    if (!runBackup) {
+                        // It's too early to back up the next thing in the queue, so bow out
+                        if (MORE_DEBUG) {
+                            Slog.i(TAG, "Device ready but too early to back up next app");
+                        }
+                        // Wait until the next app in the queue falls due for a full data backup
+                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                        break;  // we know we aren't doing work yet, so bail.
+                    }
+
+                    try {
+                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+                        headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+                        if (headBusy) {
+                            final long nextEligible = System.currentTimeMillis()
+                                    + BUSY_BACKOFF_MIN_MILLIS
+                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+                            if (DEBUG_SCHEDULING) {
+                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                Slog.i(TAG, "Full backup time but " + entry.packageName
+                                        + " is busy; deferring to "
+                                        + sdf.format(new Date(nextEligible)));
+                            }
+                            // This relocates the app's entry from the head of the queue to
+                            // its order-appropriate position further down, so upon looping
+                            // a new candidate will be considered at the head.
+                            enqueueFullBackup(entry.packageName,
+                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
+                        }
+
+                    } catch (NameNotFoundException nnf) {
+                        // So, we think we want to back this up, but it turns out the package
+                        // in question is no longer installed.  We want to drop it from the
+                        // queue entirely and move on, but if there's nothing else in the queue
+                        // we should bail entirely.  headBusy cannot have been set to true yet.
+                        runBackup = (mFullBackupQueue.size() > 1);
+                    } catch (RemoteException e) {
+                        // Cannot happen; the Activity Manager is in the same process
+                    }
+                }
+            } while (headBusy);
 
             if (!runBackup) {
                 if (DEBUG_SCHEDULING) {
@@ -4539,7 +4581,7 @@
                 return false;
             }
 
-            // Okay, the top thing is runnable now.  Pop it off and get going.
+            // Okay, the top thing is ready for backup now.  Do it.
             mFullBackupQueue.remove(0);
             CountDownLatch latch = new CountDownLatch(1);
             String[] pkg = new String[] {entry.packageName};
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 353b404..e6e69b1 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -265,8 +265,8 @@
                     Ops ops = it.next();
                     int curUid = -1;
                     try {
-                        curUid = AppGlobals.getPackageManager().getPackageUidEtc(ops.packageName,
-                                PackageManager.GET_UNINSTALLED_PACKAGES,
+                        curUid = AppGlobals.getPackageManager().getPackageUid(ops.packageName,
+                                PackageManager.MATCH_UNINSTALLED_PACKAGES,
                                 UserHandle.getUserId(ops.uidState.uid));
                     } catch (RemoteException ignored) {
                     }
@@ -691,7 +691,7 @@
         if (reqPackageName != null) {
             try {
                 reqUid = AppGlobals.getPackageManager().getPackageUid(
-                        reqPackageName, reqUserId);
+                        reqPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, reqUserId);
             } catch (RemoteException e) {
                 /* ignore - local call */
             }
@@ -1167,7 +1167,9 @@
                     int pkgUid = -1;
                     try {
                         ApplicationInfo appInfo = ActivityThread.getPackageManager()
-                                .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid));
+                                .getApplicationInfo(packageName,
+                                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                                        UserHandle.getUserId(uid));
                         if (appInfo != null) {
                             pkgUid = appInfo.uid;
                             isPrivileged = (appInfo.privateFlags
@@ -1647,7 +1649,8 @@
             if ("root".equals(packageName)) {
                 packageUid = 0;
             } else {
-                packageUid = AppGlobals.getPackageManager().getPackageUid(packageName, userId);
+                packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
             }
             if (packageUid < 0) {
                 err.println("Error: No UID for " + packageName + " in user " + userId);
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 4569dae..b5ea641 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -176,7 +176,8 @@
     private static String queryVersionName(Context context) {
         try {
             String packageName = context.getPackageName();
-            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
+            PackageInfo info = context.getPackageManager().getPackageInfo(packageName,
+                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
             return info.versionName;
         } catch (PackageManager.NameNotFoundException e) {
             Log.w(LOG_TAG, "Could not get package info", e);
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f329cff..c1a082b 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -268,8 +268,8 @@
 
         int sysUiUid = -1;
         try {
-            sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
-                    UserHandle.USER_SYSTEM);
+            sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
+                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
         } catch (PackageManager.NameNotFoundException e) {
             // Some platforms, such as wearables do not have a system ui.
             Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 65a27c8..2de5324 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1568,12 +1568,11 @@
         // load the global proxy at startup
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
 
-        // Try bringing up tracker, but if KeyStore isn't ready yet, wait
-        // for user to unlock device.
-        if (!updateLockdownVpn()) {
-            final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
-            mContext.registerReceiver(mUserPresentReceiver, filter);
-        }
+        // Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
+        // for user to unlock device too.
+        updateLockdownVpn();
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+        mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.ALL, filter, null, null);
 
         // Configure whether mobile data is always on.
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
@@ -1586,10 +1585,16 @@
     private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            // User that sent this intent = user that was just unlocked
+            final int unlockedUser = getSendingUserId();
+
             // Try creating lockdown tracker, since user present usually means
             // unlocked keystore.
-            if (updateLockdownVpn()) {
-                mContext.unregisterReceiver(this);
+            if (mUserManager.getUserInfo(unlockedUser).isPrimary() &&
+                    LockdownVpnTracker.isEnabled()) {
+                updateLockdownVpn();
+            } else {
+                updateAlwaysOnVpn(unlockedUser);
             }
         }
     };
@@ -3258,6 +3263,76 @@
         }
     }
 
+    /**
+     * Sets up or tears down the always-on VPN for user {@param user} as appropriate.
+     *
+     * @return {@code false} in case of errors; {@code true} otherwise.
+     */
+    private boolean updateAlwaysOnVpn(int user) {
+        final String lockdownPackage = getAlwaysOnVpnPackage(user);
+        if (lockdownPackage == null) {
+            return true;
+        }
+
+        // Create an intent to start the VPN service declared in the app's manifest.
+        Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
+        serviceIntent.setPackage(lockdownPackage);
+
+        try {
+            return mContext.startServiceAsUser(serviceIntent, UserHandle.of(user)) != null;
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean setAlwaysOnVpnPackage(int userId, String packageName) {
+        enforceConnectivityInternalPermission();
+        enforceCrossUserPermission(userId);
+
+        // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+        if (LockdownVpnTracker.isEnabled()) {
+            return false;
+        }
+
+        // If the current VPN package is the same as the new one, this is a no-op
+        final String oldPackage = getAlwaysOnVpnPackage(userId);
+        if (TextUtils.equals(oldPackage, packageName)) {
+            return true;
+        }
+
+        synchronized (mVpns) {
+            Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+                return false;
+            }
+            if (!vpn.setAlwaysOnPackage(packageName)) {
+                return false;
+            }
+            if (!updateAlwaysOnVpn(userId)) {
+                vpn.setAlwaysOnPackage(null);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String getAlwaysOnVpnPackage(int userId) {
+        enforceConnectivityInternalPermission();
+        enforceCrossUserPermission(userId);
+
+        synchronized (mVpns) {
+            Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+                return null;
+            }
+            return vpn.getAlwaysOnPackage();
+        }
+    }
+
     @Override
     public int checkMobileProvisioning(int suggestedTimeOutMs) {
         // TODO: Remove?  Any reason to trigger a provisioning check?
@@ -4848,6 +4923,11 @@
     }
 
     @Override
+    public String getCaptivePortalServerUrl() {
+        return NetworkMonitor.getCaptivePortalServerUrl(mContext);
+    }
+
+    @Override
     public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger,
             IBinder binder, String srcAddr, int srcPort, String dstAddr) {
         enforceKeepalivePermission();
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index f5ed83e..58a0356 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -50,11 +50,13 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IDeviceIdleController;
+import android.os.IMaintenanceActivityListener;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.Process;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -206,9 +208,13 @@
     private boolean mSyncActive;
     private boolean mJobsActive;
     private boolean mAlarmsActive;
+    private boolean mReportedMaintenanceActivity;
 
     public final AtomicFile mConfigFile;
 
+    private final RemoteCallbackList<IMaintenanceActivityListener> mMaintenanceActivityListeners =
+            new RemoteCallbackList<IMaintenanceActivityListener>();
+
     /**
      * Package names the system has white-listed to opt out of power save restrictions,
      * except for device idle mode.
@@ -813,6 +819,7 @@
     static final int MSG_REPORT_IDLE_OFF = 4;
     static final int MSG_REPORT_ACTIVE = 5;
     static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+    static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
 
     final class MyHandler extends Handler {
         MyHandler(Looper looper) {
@@ -902,6 +909,21 @@
                     int uid = msg.arg1;
                     checkTempAppWhitelistTimeout(uid);
                 } break;
+                case MSG_REPORT_MAINTENANCE_ACTIVITY: {
+                    boolean active = (msg.arg1 == 1);
+                    final int size = mMaintenanceActivityListeners.beginBroadcast();
+                    try {
+                        for (int i = 0; i < size; i++) {
+                            try {
+                                mMaintenanceActivityListeners.getBroadcastItem(i)
+                                        .onMaintenanceActivityChanged(active);
+                            } catch (RemoteException ignored) {
+                            }
+                        }
+                    } finally {
+                        mMaintenanceActivityListeners.finishBroadcast();
+                    }
+                } break;
             }
         }
     }
@@ -996,6 +1018,16 @@
             DeviceIdleController.this.downloadServiceInactive();
         }
 
+        @Override public boolean registerMaintenanceActivityListener(
+                IMaintenanceActivityListener listener) {
+            return DeviceIdleController.this.registerMaintenanceActivityListener(listener);
+        }
+
+        @Override public void unregisterMaintenanceActivityListener(
+                IMaintenanceActivityListener listener) {
+            DeviceIdleController.this.unregisterMaintenanceActivityListener(listener);
+        }
+
         @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             DeviceIdleController.this.dump(fd, pw, args);
         }
@@ -1052,12 +1084,11 @@
             for (int i=0; i<allowPowerExceptIdle.size(); i++) {
                 String pkg = allowPowerExceptIdle.valueAt(i);
                 try {
-                    ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
-                    if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
-                        int appid = UserHandle.getAppId(ai.uid);
-                        mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
-                        mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
-                    }
+                    ApplicationInfo ai = pm.getApplicationInfo(pkg,
+                            PackageManager.MATCH_SYSTEM_ONLY);
+                    int appid = UserHandle.getAppId(ai.uid);
+                    mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
+                    mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
                 } catch (PackageManager.NameNotFoundException e) {
                 }
             }
@@ -1065,16 +1096,15 @@
             for (int i=0; i<allowPower.size(); i++) {
                 String pkg = allowPower.valueAt(i);
                 try {
-                    ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
-                    if ((ai.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
-                        int appid = UserHandle.getAppId(ai.uid);
-                        // These apps are on both the whitelist-except-idle as well
-                        // as the full whitelist, so they apply in all cases.
-                        mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
-                        mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
-                        mPowerSaveWhitelistApps.put(ai.packageName, appid);
-                        mPowerSaveWhitelistSystemAppIds.put(appid, true);
-                    }
+                    ApplicationInfo ai = pm.getApplicationInfo(pkg,
+                            PackageManager.MATCH_SYSTEM_ONLY);
+                    int appid = UserHandle.getAppId(ai.uid);
+                    // These apps are on both the whitelist-except-idle as well
+                    // as the full whitelist, so they apply in all cases.
+                    mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
+                    mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
+                    mPowerSaveWhitelistApps.put(ai.packageName, appid);
+                    mPowerSaveWhitelistSystemAppIds.put(appid, true);
                 } catch (PackageManager.NameNotFoundException e) {
                 }
             }
@@ -1182,9 +1212,7 @@
         synchronized (this) {
             try {
                 ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
-                        PackageManager.GET_UNINSTALLED_PACKAGES
-                                | PackageManager.GET_DISABLED_COMPONENTS
-                                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES);
                 if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) {
                     reportPowerSaveWhitelistChangedLocked();
                     updateWhitelistAppIdsLocked();
@@ -1326,7 +1354,7 @@
     void addPowerSaveTempWhitelistAppInternal(int callingUid, String packageName,
             long duration, int userId, boolean sync, String reason) {
         try {
-            int uid = getContext().getPackageManager().getPackageUid(packageName, userId);
+            int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
             int appId = UserHandle.getAppId(uid);
             addPowerSaveTempWhitelistAppDirectInternal(callingUid, appId, duration, sync, reason);
         } catch (NameNotFoundException e) {
@@ -1708,6 +1736,7 @@
     void downloadServiceActive(IBinder token) {
         synchronized (this) {
             mDownloadServiceActive = token;
+            reportMaintenanceActivityIfNeededLocked();
             try {
                 token.linkToDeath(new IBinder.DeathRecipient() {
                     @Override public void binderDied() {
@@ -1723,6 +1752,7 @@
     void downloadServiceInactive() {
         synchronized (this) {
             mDownloadServiceActive = null;
+            reportMaintenanceActivityIfNeededLocked();
             exitMaintenanceEarlyIfNeededLocked();
         }
     }
@@ -1730,6 +1760,7 @@
     void setSyncActive(boolean active) {
         synchronized (this) {
             mSyncActive = active;
+            reportMaintenanceActivityIfNeededLocked();
             if (!active) {
                 exitMaintenanceEarlyIfNeededLocked();
             }
@@ -1739,6 +1770,7 @@
     void setJobsActive(boolean active) {
         synchronized (this) {
             mJobsActive = active;
+            reportMaintenanceActivityIfNeededLocked();
             if (!active) {
                 exitMaintenanceEarlyIfNeededLocked();
             }
@@ -1754,6 +1786,30 @@
         }
     }
 
+    boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener) {
+        synchronized (this) {
+            mMaintenanceActivityListeners.register(listener);
+            return mReportedMaintenanceActivity;
+        }
+    }
+
+    void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener) {
+        synchronized (this) {
+            mMaintenanceActivityListeners.unregister(listener);
+        }
+    }
+
+    void reportMaintenanceActivityIfNeededLocked() {
+        boolean active = mJobsActive | mSyncActive | (mDownloadServiceActive != null);
+        if (active == mReportedMaintenanceActivity) {
+            return;
+        }
+        mReportedMaintenanceActivity = active;
+        Message msg = mHandler.obtainMessage(MSG_REPORT_MAINTENANCE_ACTIVITY,
+                mReportedMaintenanceActivity ? 1 : 0, 0);
+        mHandler.sendMessage(msg);
+    }
+
     void exitMaintenanceEarlyIfNeededLocked() {
         if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE) {
             if (mActiveIdleOpCount <= 0 && mDownloadServiceActive == null
@@ -2010,9 +2066,7 @@
                     if (name != null) {
                         try {
                             ApplicationInfo ai = pm.getApplicationInfo(name,
-                                    PackageManager.GET_UNINSTALLED_PACKAGES
-                                            | PackageManager.GET_DISABLED_COMPONENTS
-                                            | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+                                    PackageManager.MATCH_UNINSTALLED_PACKAGES);
                             mPowerSaveWhitelistUserApps.put(ai.packageName,
                                     UserHandle.getAppId(ai.uid));
                         } catch (PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 3fdef1d..044bb04 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -16,9 +16,8 @@
 
 package com.android.server;
 
+import android.app.AppOpsManager;
 import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.MemoryFile;
@@ -66,6 +65,7 @@
     private static final int HISTORY_SIZE = 20;
 
     private final Context mContext;
+    private final AppOpsManager mAppOps;
     private final Object mLock = new Object();
     private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
     private HistoricalData[] mHistoricalLog = new HistoricalData[HISTORY_SIZE];
@@ -74,15 +74,7 @@
 
     public GraphicsStatsService(Context context) {
         mContext = context;
-    }
-
-    private boolean isValid(int uid, String packageName) {
-        try {
-            PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
-            return info.applicationInfo.uid == uid;
-        } catch (NameNotFoundException e) {
-        }
-        return false;
+        mAppOps = context.getSystemService(AppOpsManager.class);
     }
 
     @Override
@@ -93,9 +85,7 @@
         ParcelFileDescriptor pfd = null;
         long callingIdentity = Binder.clearCallingIdentity();
         try {
-            if (!isValid(uid, packageName)) {
-                throw new RemoteException("Invalid package name");
-            }
+            mAppOps.checkPackage(uid, packageName);
             synchronized (mLock) {
                 pfd = requestBufferForProcessLocked(token, uid, pid, packageName);
             }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 04abcca..15b5502 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2383,10 +2383,6 @@
             return;
         }
         synchronized (mMethodMap) {
-            if (mCurClient == null || client == null
-                || mCurClient.client.asBinder() != client.asBinder()) {
-                Slog.w(TAG, "Ignoring showInputMethodAndSubtypeEnablerFromClient of: " + client);
-            }
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
                     MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
         }
@@ -2716,9 +2712,7 @@
                 return true;
 
             case MSG_SHOW_IM_SUBTYPE_ENABLER:
-                args = (SomeArgs)msg.obj;
-                showInputMethodAndSubtypeEnabler((String)args.arg1);
-                args.recycle();
+                showInputMethodAndSubtypeEnabler((String)msg.obj);
                 return true;
 
             case MSG_SHOW_IM_CONFIG:
@@ -3005,7 +2999,11 @@
         if (!TextUtils.isEmpty(inputMethodId)) {
             intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
         }
-        mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
+        final int userId;
+        synchronized (mMethodMap) {
+            userId = mSettings.getCurrentUserId();
+        }
+        mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
     }
 
     private void showConfigureInputMethods() {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 1a21f5c..377d52f 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -695,7 +695,7 @@
             unlockUser(userId, token);
 
             UserInfo info = UserManager.get(mContext).getUserInfo(userId);
-            if (LockPatternUtils.isSeparateWorkChallengeEnabled() && info.isManagedProfile()) {
+            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
                 TrustManager trustManager =
                         (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
                 trustManager.setDeviceLockedForUser(userId, false);
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index 137fa27..816c791 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -71,6 +71,7 @@
     private final Object mFileWriteLock = new Object();
 
     private int mStoredCredentialType;
+    private LockPatternUtils mLockPatternUtils;
 
     class CredentialHash {
         static final int TYPE_NONE = -1;
@@ -100,6 +101,7 @@
     public LockSettingsStorage(Context context, Callback callback) {
         mContext = context;
         mOpenHelper = new DatabaseHelper(context, callback);
+        mLockPatternUtils = new LockPatternUtils(context);
     }
 
     public void writeKeyValue(String key, String value, int userId) {
@@ -388,7 +390,7 @@
 
     private int getUserParentOrSelfId(int userId) {
         // Device supports per user encryption, so lock is applied to the given user.
-        if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
             return userId;
         }
         // Device uses Block Based Encryption, and the parent user's lock is used for the whole
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 6cccf38..4a186a6 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2355,7 +2355,8 @@
             return false;
         }
 
-        final int packageUid = mPms.getPackageUid(packageName, UserHandle.getUserId(callerUid));
+        final int packageUid = mPms.getPackageUid(packageName,
+                PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid));
 
         if (DEBUG_OBB) {
             Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index e8b90d8..a291cc7 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -94,7 +94,8 @@
         PackageManager pm = mContext.getPackageManager();
         int allowedUid = -1;
         try {
-            allowedUid = pm.getPackageUid(allowedPackage, userHandle);
+            allowedUid = pm.getPackageUidAsUser(allowedPackage,
+                    PackageManager.MATCH_SYSTEM_ONLY, userHandle);
         } catch (PackageManager.NameNotFoundException e) {
             // not expected
             Slog.e(TAG, "not able to find package " + allowedPackage, e);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index aa99442..0f12818 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2312,6 +2312,14 @@
         }
 
         final int uid = Binder.getCallingUid();
+        // Only allow system to start session
+        if (!isSystemUid(uid)) {
+            String msg = String.format(
+                    "uid %s cannot stat add account session.",
+                    uid);
+            throw new SecurityException(msg);
+        }
+
         final int userId = UserHandle.getUserId(uid);
         if (!canUserModifyAccounts(userId, uid)) {
             try {
@@ -2499,6 +2507,14 @@
         }
 
         final int uid = Binder.getCallingUid();
+        // Only allow system to finish session
+        if (!isSystemUid(uid)) {
+            String msg = String.format(
+                    "uid %s cannot finish session.",
+                    uid);
+            throw new SecurityException(msg);
+        }
+
         final int userId = UserHandle.getUserId(uid);
         if (!canUserModifyAccounts(userId, uid)) {
             sendErrorResponse(response,
@@ -2717,6 +2733,16 @@
         if (account == null) {
             throw new IllegalArgumentException("account is null");
         }
+
+        final int uid = Binder.getCallingUid();
+        // Only allow system to start session
+        if (!isSystemUid(uid)) {
+            String msg = String.format(
+                    "uid %s cannot start update credentials session.",
+                    uid);
+            throw new SecurityException(msg);
+        }
+
         int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
@@ -2793,6 +2819,25 @@
         }
     }
 
+    @Override
+    public boolean someUserHasAccount(@NonNull final Account account) {
+        if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
+            throw new SecurityException("Only system can check for accounts across users");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            AccountAndUser[] allAccounts = getAllAccounts();
+            for (int i = allAccounts.length - 1; i >= 0; i--) {
+                if (allAccounts[i].account.equals(account)) {
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     private class GetAccountsByTypeAndFeatureSession extends Session {
         private final String[] mFeatures;
         private volatile Account[] mAccountsOfType = null;
@@ -3174,7 +3219,8 @@
         int packageUid = -1;
         try {
             packageUid = AppGlobals.getPackageManager().getPackageUid(
-                    packageName, UserHandle.getCallingUserId());
+                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                    UserHandle.getCallingUserId());
         } catch (RemoteException re) {
             Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
             return new Account[0];
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4348913..7ba6338 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -61,6 +61,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -326,7 +327,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, resolvedType, callingPackage,
-                    callingPid, callingUid, userId, true, callerFg);
+                    callingPid, callingUid, userId, true, callerFg, false);
         if (res == null) {
             return null;
         }
@@ -549,7 +550,7 @@
 
         // If this service is active, make sure it is stopped.
         ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, null,
-                Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false);
+                Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false);
         if (r != null) {
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
@@ -598,7 +599,7 @@
     IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
         ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, callingPackage,
                 Binder.getCallingPid(), Binder.getCallingUid(),
-                UserHandle.getCallingUserId(), false, false);
+                UserHandle.getCallingUserId(), false, false, false);
 
         IBinder ret = null;
         if (r != null) {
@@ -831,10 +832,11 @@
         }
 
         final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
+        final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
 
         ServiceLookupResult res =
-            retrieveServiceLocked(service, resolvedType, callingPackage,
-                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
+            retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
         if (res == null) {
             return 0;
         }
@@ -1192,7 +1194,7 @@
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
             String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
-            boolean createIfNeeded, boolean callingFromFg) {
+            boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal) {
         ServiceRecord r = null;
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
                 + " type=" + resolvedType + " callingUid=" + callingUid);
@@ -1205,16 +1207,23 @@
         if (comp != null) {
             r = smap.mServicesByName.get(comp);
         }
-        if (r == null) {
+        if (r == null && !isBindExternal) {
             Intent.FilterComparison filter = new Intent.FilterComparison(service);
             r = smap.mServicesByIntent.get(filter);
         }
+        if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
+                && !callingPackage.equals(r.packageName)) {
+            // If an external service is running within its own package, other packages
+            // should not bind to that instance.
+            r = null;
+        }
         if (r == null) {
             try {
-                ResolveInfo rInfo =
-                    AppGlobals.getPackageManager().resolveService(
-                                service, resolvedType,
-                                ActivityManagerService.STOCK_PM_FLAGS, userId);
+                // TODO: come back and remove this assumption to triage all services
+                ResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service,
+                        resolvedType, ActivityManagerService.STOCK_PM_FLAGS
+                                | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                        userId);
                 ServiceInfo sInfo =
                     rInfo != null ? rInfo.serviceInfo : null;
                 if (sInfo == null) {
@@ -1224,6 +1233,37 @@
                 }
                 ComponentName name = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
+                if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
+                    if (isBindExternal) {
+                        if (!sInfo.exported) {
+                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
+                                    " is not exported");
+                        }
+                        if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
+                                    " is not an isolatedProcess");
+                        }
+                        // Run the service under the calling package's application.
+                        ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+                                callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
+                        if (aInfo == null) {
+                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
+                                    "could not resolve client package " + callingPackage);
+                        }
+                        sInfo = new ServiceInfo(sInfo);
+                        sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
+                        sInfo.applicationInfo.packageName = aInfo.packageName;
+                        sInfo.applicationInfo.uid = aInfo.uid;
+                        name = new ComponentName(aInfo.packageName, name.getClassName());
+                        service.setComponent(name);
+                    } else {
+                        throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
+                                name);
+                    }
+                } else if (isBindExternal) {
+                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
+                            " is not an externalService");
+                }
                 if (userId > 0) {
                     if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
                             sInfo.name, sInfo.flags)
@@ -1244,7 +1284,7 @@
                                 sInfo.applicationInfo.uid, sInfo.packageName, callingPid);
                         if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                             Slog.w(TAG, "Background execution not allowed: service "
-                                    + r.intent + " to " + name.flattenToShortString()
+                                    + service + " to " + name.flattenToShortString()
                                     + " from pid=" + callingPid + " uid=" + callingUid
                                     + " pkg=" + callingPackage);
                             return null;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e8dc4e..1d9bd91 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,6 @@
 
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
-
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.AssistUtils;
@@ -35,6 +34,7 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -201,6 +201,7 @@
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
@@ -239,11 +240,13 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 import dalvik.system.VMRuntime;
+
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
@@ -254,9 +257,15 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.GET_PROVIDERS;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
@@ -510,9 +519,9 @@
     private Installer mInstaller;
 
     /** Run all ActivityStacks through this */
-    ActivityStackSupervisor mStackSupervisor;
+    final ActivityStackSupervisor mStackSupervisor;
 
-    ActivityStarter mActivityStarter;
+    final ActivityStarter mActivityStarter;
 
     /** Task stack change listeners. */
     private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
@@ -557,7 +566,7 @@
     /**
      * List of intents that were used to start the most recent tasks.
      */
-    private final RecentTasks mRecentTasks;
+    final RecentTasks mRecentTasks;
 
     /**
      * For addAppTask: cached of the last activity component that was added.
@@ -1045,11 +1054,6 @@
     final AppOpsService mAppOpsService;
 
     /**
-     * Save recent tasks information across reboots.
-     */
-    final TaskPersister mTaskPersister;
-
-    /**
      * Current configuration information.  HistoryRecord objects are given
      * a reference to this object to indicate which configuration they are
      * currently running in, so this object must be kept immutable.
@@ -1301,7 +1305,7 @@
     int mMemWatchDumpUid;
     String mTrackAllocationApp = null;
 
-    final long[] mTmpLong = new long[1];
+    final long[] mTmpLong = new long[2];
 
     static final class ProcessChangeItem {
         static final int CHANGE_ACTIVITIES = 1<<0;
@@ -1445,6 +1449,7 @@
     static final int SYSTEM_USER_UNLOCK_MSG = 61;
     static final int LOG_STACK_STATE = 62;
     static final int VR_MODE_CHANGE_MSG = 63;
+    static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1948,7 +1953,10 @@
                 break;
             }
             case SYSTEM_USER_UNLOCK_MSG: {
-                mSystemServiceManager.unlockUser(msg.arg1);
+                final int userId = msg.arg1;
+                mSystemServiceManager.unlockUser(userId);
+                mRecentTasks.cleanupLocked(userId);
+                installEncryptionUnawareProviders(userId);
                 break;
             }
             case SYSTEM_USER_CURRENT_MSG: {
@@ -1998,9 +2006,7 @@
             }
             case NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    int i = mTaskStackListeners.beginBroadcast();
-                    while (i > 0) {
-                        i--;
+                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
                         try {
                             // Make a one-way callback to the listener
                             mTaskStackListeners.getBroadcastItem(i).onTaskStackChanged();
@@ -2012,6 +2018,20 @@
                 }
                 break;
             }
+            case NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+                        try {
+                            // Make a one-way callback to the listener
+                            mTaskStackListeners.getBroadcastItem(i).onActivityPinned();
+                        } catch (RemoteException e){
+                            // Handled by the RemoteCallbackList
+                        }
+                    }
+                    mTaskStackListeners.finishBroadcast();
+                }
+                break;
+            }
             case NOTIFY_CLEARTEXT_NETWORK_MSG: {
                 final int uid = msg.arg1;
                 final byte[] firstPacket = (byte[]) msg.obj;
@@ -2218,7 +2238,7 @@
                 }
 
                 int num = 0;
-                long[] tmp = new long[1];
+                long[] tmp = new long[2];
                 do {
                     ProcessRecord proc;
                     int procState;
@@ -2250,7 +2270,7 @@
                             if (pss != 0 && proc.thread != null && proc.setProcState == procState
                                     && proc.pid == pid && proc.lastPssTime == lastPssTime) {
                                 num++;
-                                recordPssSampleLocked(proc, procState, pss, tmp[0],
+                                recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1],
                                         SystemClock.uptimeMillis());
                             }
                         }
@@ -2275,7 +2295,7 @@
             ServiceManager.addService("processinfo", new ProcessInfoService(this));
 
             ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
-                    "android", STOCK_PM_FLAGS);
+                    "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
             mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
 
             synchronized (this) {
@@ -2485,10 +2505,10 @@
 
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
-        mRecentTasks = new RecentTasks(this);
-        mStackSupervisor = new ActivityStackSupervisor(this, mRecentTasks);
+        mStackSupervisor = new ActivityStackSupervisor(this);
         mActivityStarter = new ActivityStarter(this, mStackSupervisor);
-        mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, mRecentTasks);
+        mRecentTasks = new RecentTasks(this, mStackSupervisor);
+
 
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
@@ -2542,6 +2562,10 @@
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
     }
 
+    void onUserStoppedLocked(int userId) {
+        mRecentTasks.unloadUserRecentsLocked(userId);
+    }
+
     public void initPowerManagement() {
         mStackSupervisor.initPowerManagement();
         mBatteryStatsService.initPowerManagement();
@@ -2743,9 +2767,14 @@
         return mAppBindArgs;
     }
 
-    final void setFocusedActivityLocked(ActivityRecord r, String reason) {
+    boolean setFocusedActivityLocked(ActivityRecord r, String reason) {
         if (r == null || mFocusedActivity == r) {
-            return;
+            return false;
+        }
+
+        if (!r.isFocusable()) {
+            if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: unfocusable r=" + r);
+            return false;
         }
 
         if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
@@ -2783,7 +2812,7 @@
                 finishVoiceTask(last.task.voiceSession);
             }
         }
-        if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) {
+        if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) {
             mWindowManager.setFocusedApp(r.appToken, true);
         }
         applyUpdateLockStateLocked(r);
@@ -2799,23 +2828,35 @@
                 mFocusedActivity == null ? -1 : mFocusedActivity.userId,
                 mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName,
                 reason);
+        return true;
     }
 
-    final void clearFocusedActivity(ActivityRecord r) {
-        if (mFocusedActivity == r) {
-            ActivityStack stack = mStackSupervisor.getFocusedStack();
-            if (stack != null) {
-                ActivityRecord top = stack.topActivity();
-                if (top != null && top.userId != mLastFocusedUserId) {
-                    mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
-                    mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG,
-                                    top.userId, 0));
-                    mLastFocusedUserId = top.userId;
-                }
-            }
-            mFocusedActivity = null;
-            EventLogTags.writeAmFocusedActivity(-1, "NULL", "clearFocusedActivity");
+    final void resetFocusedActivityIfNeededLocked(ActivityRecord goingAway) {
+        if (mFocusedActivity != goingAway) {
+            return;
         }
+
+        final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
+        if (focusedStack != null) {
+            final ActivityRecord top = focusedStack.topActivity();
+            if (top != null && top.userId != mLastFocusedUserId) {
+                mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, top.userId, 0));
+                mLastFocusedUserId = top.userId;
+            }
+        }
+
+        // Try to move focus to another activity if possible.
+        if (setFocusedActivityLocked(
+                focusedStack.topRunningActivityLocked(), "resetFocusedActivityIfNeeded")) {
+            return;
+        }
+
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "resetFocusedActivityIfNeeded: Setting focus to NULL "
+                + "prev mFocusedActivity=" + mFocusedActivity + " goingAway=" + goingAway);
+        mFocusedActivity = null;
+        EventLogTags.writeAmFocusedActivity(-1, "NULL", "resetFocusedActivityIfNeeded");
     }
 
     @Override
@@ -2827,7 +2868,7 @@
                 ActivityRecord r = stack.topRunningActivityLocked();
                 if (r != null) {
                     setFocusedActivityLocked(r, "setFocusedStack");
-                    mStackSupervisor.resumeTopActivitiesLocked(stack, null, null);
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 }
             }
         }
@@ -2841,10 +2882,9 @@
             synchronized (ActivityManagerService.this) {
                 TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
                 if (task != null) {
-                    ActivityRecord r = task.topRunningActivityLocked();
-                    if (r != null) {
-                        setFocusedActivityLocked(r, "setFocusedTask");
-                        mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
+                    final ActivityRecord r = task.topRunningActivityLocked();
+                    if (setFocusedActivityLocked(r, "setFocusedTask")) {
+                        mStackSupervisor.resumeFocusedStackTopActivityLocked();
                     }
                 }
             }
@@ -2856,7 +2896,8 @@
     /** Sets the task stack listener that gets callbacks when a task stack changes. */
     @Override
     public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
-        synchronized (ActivityManagerService.this) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "registerTaskStackListener()");
+        synchronized (this) {
             if (listener != null) {
                 mTaskStackListeners.register(listener);
             }
@@ -3410,7 +3451,8 @@
                 try {
                     checkTime(startTime, "startProcess: getting gids from package manager");
                     final IPackageManager pm = AppGlobals.getPackageManager();
-                    permGids = pm.getPackageGids(app.info.packageName, app.userId);
+                    permGids = pm.getPackageGids(app.info.packageName,
+                            MATCH_DEBUG_TRIAGED_MISSING, app.userId);
                     MountServiceInternal mountServiceInternal = LocalServices.getService(
                             MountServiceInternal.class);
                     mountExternal = mountServiceInternal.getExternalStorageMountMode(uid,
@@ -3590,7 +3632,7 @@
     Intent getHomeIntent() {
         Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
         intent.setComponent(mTopComponent);
-        intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED);
+        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
         if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
             intent.addCategory(Intent.CATEGORY_HOME);
         }
@@ -3606,11 +3648,9 @@
             return false;
         }
         Intent intent = getHomeIntent();
-        ActivityInfo aInfo =
-            resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
+        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
         if (aInfo != null) {
-            intent.setComponent(new ComponentName(
-                    aInfo.applicationInfo.packageName, aInfo.name));
+            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
             // Don't do this if the home app is currently being
             // instrumented.
             aInfo = new ActivityInfo(aInfo);
@@ -3669,21 +3709,11 @@
             mCheckedForSetup = true;
 
             // See if we should be showing the platform update setup UI.
-            Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
-            List<ResolveInfo> ris = mContext.getPackageManager()
-                    .queryIntentActivities(intent, PackageManager.GET_META_DATA);
-
-            // We don't allow third party apps to replace this.
-            ResolveInfo ri = null;
-            for (int i=0; ris != null && i<ris.size(); i++) {
-                if ((ris.get(i).activityInfo.applicationInfo.flags
-                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    ri = ris.get(i);
-                    break;
-                }
-            }
-
-            if (ri != null) {
+            final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
+            final List<ResolveInfo> ris = mContext.getPackageManager().queryIntentActivities(intent,
+                    PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+            if (!ris.isEmpty()) {
+                final ResolveInfo ri = ris.get(0);
                 String vers = ri.activityInfo.metaData != null
                         ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
                         : null;
@@ -4342,7 +4372,7 @@
     }
 
     @Override
-    public final int startActivityFromRecents(int taskId, int launchStackId, Bundle bOptions) {
+    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
         if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: startActivityFromRecents called without " +
                     START_TASKS_FROM_RECENTS;
@@ -4351,24 +4381,28 @@
         }
         final long origId = Binder.clearCallingIdentity();
         try {
-            return startActivityFromRecentsInner(taskId, launchStackId, bOptions);
+            return startActivityFromRecentsInner(taskId, bOptions);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
-    final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle bOptions) {
+    final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
         final TaskRecord task;
         final int callingUid;
         final String callingPackage;
         final Intent intent;
         final int userId;
         synchronized (this) {
+            final ActivityOptions activityOptions = (bOptions != null)
+                    ? new ActivityOptions(bOptions) : null;
+            final int launchStackId = (activityOptions != null)
+                    ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
+
             if (launchStackId == HOME_STACK_ID) {
                 throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
                         + taskId + " can't be launch in the home stack.");
             }
-
             task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
             if (task == null) {
                 throw new IllegalArgumentException(
@@ -4376,10 +4410,9 @@
             }
 
             if (launchStackId != INVALID_STACK_ID) {
-                if (launchStackId == DOCKED_STACK_ID && bOptions != null) {
-                    ActivityOptions activityOptions = new ActivityOptions(bOptions);
-                    mWindowManager.setDockedStackCreateState(activityOptions.getDockCreateMode(),
-                            null /* initialBounds */);
+                if (launchStackId == DOCKED_STACK_ID && activityOptions != null) {
+                    mWindowManager.setDockedStackCreateState(
+                            activityOptions.getDockCreateMode(), null /* initialBounds */);
                 }
                 if (task.stack.mStackId != launchStackId) {
                     mStackSupervisor.moveTaskToStackLocked(
@@ -4388,7 +4421,10 @@
                 }
             }
 
-            if (task.getRootActivity() != null) {
+            // If the user must confirm credentials (e.g. when first launching a work app and the
+            // Work Challenge is present) let startActivityInPackage handle the intercepting.
+            if (!mUserController.shouldConfirmCredentials(task.userId)
+                    && task.getRootActivity() != null) {
                 moveTaskToFrontLocked(task.taskId, 0, bOptions);
                 return ActivityManager.START_TASK_TO_FRONT;
             }
@@ -4473,7 +4509,7 @@
             if (config != null) {
                 r.frozenBeforeDestroy = true;
                 if (!updateConfigurationLocked(config, r, false)) {
-                    mStackSupervisor.resumeTopActivitiesLocked();
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 }
             }
             Binder.restoreCallingIdentity(origId);
@@ -4811,12 +4847,11 @@
             finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
         }
 
-        if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
-            // If there was nothing to resume, and we are not already
-            // restarting this process, but there is a visible activity that
-            // is hosted by the process...  then make sure all visible
-            // activities are running, taking care of restarting this
-            // process.
+        if (!restarting && hasVisibleActivities
+                && !mStackSupervisor.resumeFocusedStackTopActivityLocked()) {
+            // If there was nothing to resume, and we are not already restarting this process, but
+            // there is a visible activity that is hosted by the process...  then make sure all
+            // visible activities are running, taking care of restarting this process.
             mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         }
     }
@@ -5346,7 +5381,7 @@
             int pkgUid = -1;
             synchronized(this) {
                 try {
-                    pkgUid = pm.getPackageUid(packageName, userId);
+                    pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES, userId);
                 } catch (RemoteException e) {
                 }
                 if (pkgUid == -1) {
@@ -5431,7 +5466,8 @@
             synchronized(this) {
                 int appId = -1;
                 try {
-                    appId = UserHandle.getAppId(pm.getPackageUid(packageName, 0));
+                    appId = UserHandle.getAppId(
+                            pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId));
                 } catch (RemoteException e) {
                 }
                 if (appId == -1) {
@@ -5517,7 +5553,8 @@
                 for (int user : users) {
                     int pkgUid = -1;
                     try {
-                        pkgUid = pm.getPackageUid(packageName, user);
+                        pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
+                                user);
                     } catch (RemoteException e) {
                     }
                     if (pkgUid == -1) {
@@ -5873,7 +5910,7 @@
         // Clean-up disabled activities.
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
                 packageName, disabledClasses, true, false, userId) && mBooted) {
-            mStackSupervisor.resumeTopActivitiesLocked();
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
             mStackSupervisor.scheduleIdleLocked();
         }
 
@@ -5911,8 +5948,8 @@
 
         if (appId < 0 && packageName != null) {
             try {
-                appId = UserHandle.getAppId(
-                        AppGlobals.getPackageManager().getPackageUid(packageName, 0));
+                appId = UserHandle.getAppId(AppGlobals.getPackageManager()
+                        .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId));
             } catch (RemoteException e) {
             }
         }
@@ -6061,7 +6098,7 @@
                 }
             }
             if (mBooted) {
-                mStackSupervisor.resumeTopActivitiesLocked();
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 mStackSupervisor.scheduleIdleLocked();
             }
         }
@@ -6554,8 +6591,10 @@
             Process.establishZygoteConnectionForAbi(abi);
             final String instructionSet = VMRuntime.getInstructionSet(abi);
             if (!completedIsas.contains(instructionSet)) {
-                if (mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi)) != 0) {
-                    Slog.e(TAG, "Unable to mark boot complete for abi: " + abi);
+                try {
+                    mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi));
+                } catch (InstallerException e) {
+                    Slog.e(TAG, "Unable to mark boot complete for abi: " + abi, e);
                 }
                 completedIsas.add(instructionSet);
             }
@@ -6736,6 +6775,15 @@
     }
 
     @Override
+    public final void activityRelaunched(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (this) {
+            mStackSupervisor.activityRelaunchedLocked(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
     public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
             int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
@@ -6883,8 +6931,8 @@
             }
             try {
                 if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
-                    int uid = AppGlobals.getPackageManager()
-                            .getPackageUid(packageName, UserHandle.getUserId(callingUid));
+                    final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
+                            MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
                     if (!UserHandle.isSameApp(callingUid, uid)) {
                         String msg = "Permission Denial: getIntentSender() from pid="
                             + Binder.getCallingPid()
@@ -6979,8 +7027,8 @@
         synchronized(this) {
             PendingIntentRecord rec = (PendingIntentRecord)sender;
             try {
-                int uid = AppGlobals.getPackageManager()
-                        .getPackageUid(rec.key.packageName, UserHandle.getCallingUserId());
+                final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
+                        MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
                 if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
                     String msg = "Permission Denial: cancelIntentSender() from pid="
                         + Binder.getCallingPid()
@@ -7208,6 +7256,17 @@
     }
 
     @Override
+    public boolean isAppForeground(int uid) throws RemoteException {
+        synchronized (this) {
+            UidRecord uidRec = mActiveUids.get(uid);
+            if (uidRec == null || uidRec.idle) {
+                return false;
+            }
+            return uidRec.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+        }
+    }
+
+    @Override
     public boolean inMultiWindowMode(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -7257,7 +7316,7 @@
                             + "Can't find activity for token=" + token);
                 }
 
-                if (!r.info.supportsPip) {
+                if (!r.supportsPictureInPicture()) {
                     throw new IllegalArgumentException("enterPictureInPictureMode: "
                             + "Picture-In-Picture not supported for r=" + r);
                 }
@@ -7727,7 +7786,8 @@
         int targetUid = lastTargetUid;
         if (targetUid < 0 && targetPkg != null) {
             try {
-                targetUid = pm.getPackageUid(targetPkg, UserHandle.getUserId(callingUid));
+                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+                        UserHandle.getUserId(callingUid));
                 if (targetUid < 0) {
                     if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                             "Can't grant URI permission no uid for: " + targetPkg);
@@ -7865,7 +7925,7 @@
         int targetUid;
         final IPackageManager pm = AppGlobals.getPackageManager();
         try {
-            targetUid = pm.getPackageUid(targetPkg, targetUserId);
+            targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
         } catch (RemoteException ex) {
             return;
         }
@@ -7926,7 +7986,8 @@
             targetUid = needed.targetUid;
         } else {
             try {
-                targetUid = pm.getPackageUid(targetPkg, targetUserId);
+                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+                        targetUserId);
             } catch (RemoteException ex) {
                 return null;
             }
@@ -8393,8 +8454,8 @@
                         if (pi != null && sourcePkg.equals(pi.packageName)) {
                             int targetUid = -1;
                             try {
-                                targetUid = AppGlobals.getPackageManager()
-                                        .getPackageUid(targetPkg, targetUserId);
+                                targetUid = AppGlobals.getPackageManager().getPackageUid(
+                                        targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
                             } catch (RemoteException e) {
                             }
                             if (targetUid != -1) {
@@ -8550,7 +8611,8 @@
         final int callingUid = Binder.getCallingUid();
         final IPackageManager pm = AppGlobals.getPackageManager();
         try {
-            final int packageUid = pm.getPackageUid(packageName, UserHandle.getUserId(callingUid));
+            final int packageUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
+                    UserHandle.getUserId(callingUid));
             if (packageUid != callingUid) {
                 throw new SecurityException(
                         "Package " + packageName + " does not belong to calling UID " + callingUid);
@@ -8590,6 +8652,35 @@
     }
 
     @Override
+    public ParceledListSlice<android.content.UriPermission> getGrantedUriPermissions(
+            String packageName, int userId) {
+        enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
+                "getGrantedUriPermissions");
+
+        final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
+        synchronized (this) {
+            final int size = mGrantedUriPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+                for (UriPermission perm : perms.values()) {
+                    if (packageName.equals(perm.targetPkg) && perm.targetUserId == userId
+                            && perm.persistedModeFlags != 0) {
+                        result.add(perm.buildPersistedPublicApiObject());
+                    }
+                }
+            }
+        }
+        return new ParceledListSlice<android.content.UriPermission>(result);
+    }
+
+    @Override
+    public void clearGrantedUriPermissions(String packageName, int userId) {
+        enforceCallingPermission(android.Manifest.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS,
+                "clearGrantedUriPermissions");
+        removeUriPermissionsForPackageLocked(packageName, userId, true);
+    }
+
+    @Override
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
         synchronized (this) {
             ProcessRecord app =
@@ -8771,6 +8862,8 @@
                     android.Manifest.permission.GET_DETAILED_TASKS)
                     == PackageManager.PERMISSION_GRANTED;
 
+            mRecentTasks.loadUserRecentsLocked(userId);
+
             final int recentsCount = mRecentTasks.size();
             ArrayList<ActivityManager.RecentTaskInfo> res =
                     new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
@@ -8933,8 +9026,9 @@
                 thumbnailInfo.taskHeight = displaySize.y;
                 thumbnailInfo.screenOrientation = mConfiguration.orientation;
 
-                TaskRecord task = new TaskRecord(this, mStackSupervisor.getNextTaskId(), ainfo,
-                        intent, description, thumbnailInfo);
+                TaskRecord task = new TaskRecord(this,
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
+                        ainfo, intent, description, thumbnailInfo);
 
                 int trimIdx = mRecentTasks.trimForTaskLocked(task, false);
                 if (trimIdx >= 0) {
@@ -8994,15 +9088,14 @@
                 task.mResizeable = resizeable;
                 mWindowManager.setTaskResizeable(taskId, resizeable);
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mStackSupervisor.resumeTopActivitiesLocked();
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
             }
         }
     }
 
     @Override
     public void resizeTask(int taskId, Rect bounds, int resizeMode) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "resizeTask()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -9052,8 +9145,7 @@
 
     @Override
     public Rect getTaskBounds(int taskId) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "getTaskBounds()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
         long ident = Binder.clearCallingIdentity();
         Rect rect = new Rect();
         try {
@@ -9095,9 +9187,10 @@
                 passedIconFile.getName());
         if (!legitIconFile.getPath().equals(filePath)
                 || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
-            throw new IllegalArgumentException("Bad file path: " + filePath);
+            throw new IllegalArgumentException("Bad file path: " + filePath
+                    + " passed for userId " + userId);
         }
-        return mTaskPersister.getTaskDescriptionIcon(filePath);
+        return mRecentTasks.getTaskDescriptionIcon(filePath);
     }
 
     @Override
@@ -9355,8 +9448,7 @@
     @Override
     public IActivityContainer createVirtualActivityContainer(IBinder parentActivityToken,
             IActivityContainerCallback callback) throws RemoteException {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "createActivityContainer()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createActivityContainer()");
         synchronized (this) {
             if (parentActivityToken == null) {
                 throw new IllegalArgumentException("parent token must not be null");
@@ -9374,8 +9466,7 @@
 
     @Override
     public void deleteActivityContainer(IActivityContainer container) throws RemoteException {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "deleteActivityContainer()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "deleteActivityContainer()");
         synchronized (this) {
             mStackSupervisor.deleteActivityContainer(container);
         }
@@ -9383,8 +9474,7 @@
 
     @Override
     public IActivityContainer createStackOnDisplay(int displayId) throws RemoteException {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "createStackOnDisplay()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
         synchronized (this) {
             final int stackId = mStackSupervisor.getNextStackId();
             final ActivityStack stack =
@@ -9419,23 +9509,23 @@
     }
 
     @Override
-    public void moveActivityToStack(IBinder token, int stackId) throws RemoteException {
-        if (stackId == HOME_STACK_ID) {
-            throw new IllegalArgumentException(
-                    "moveActivityToStack: Attempt to move token " + token + " to home stack");
-        }
+    public void exitFreeformMode(IBinder token) throws RemoteException {
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
                 final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                 if (r == null) {
                     throw new IllegalArgumentException(
-                            "moveActivityToStack: No activity record matching token=" + token);
+                            "exitFreeformMode: No activity record matching token=" + token);
                 }
-                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r
-                        + " to stackId=" + stackId);
-                mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, !FORCE_FOCUS,
-                        "moveActivityToStack", ANIMATE);
+                final ActivityStack stack = r.getStackLocked(token);
+                if (stack == null || stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
+                    throw new IllegalStateException(
+                            "exitFreeformMode: You can only go fullscreen from freeform.");
+                }
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
+                mStackSupervisor.moveTaskToStackLocked(r.task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+                        ON_TOP, !FORCE_FOCUS, "exitFreeformMode", ANIMATE);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -9444,8 +9534,7 @@
 
     @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "moveTaskToStack()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
         if (stackId == HOME_STACK_ID) {
             throw new IllegalArgumentException(
                     "moveTaskToStack: Attempt to move task " + taskId + " to home stack");
@@ -9480,8 +9569,7 @@
     @Override
     public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
             Rect initialBounds) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "moveTaskToDockedStack()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
@@ -9507,8 +9595,7 @@
      */
     @Override
     public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "moveTopActivityToPinnedStack()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTopActivityToPinnedStack()");
         synchronized (this) {
             if (!mSupportsPictureInPicture) {
                 throw new IllegalStateException("moveTopActivityToPinnedStack:"
@@ -9526,13 +9613,31 @@
 
     @Override
     public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "resizeStack()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
                 mStackSupervisor.resizeStackLocked(
-                        stackId, bounds, !PRESERVE_WINDOWS, allowResizeInDockedMode);
+                        stackId, bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                        !PRESERVE_WINDOWS, allowResizeInDockedMode);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "resizeDockedStack()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
+                        tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
+                        PRESERVE_WINDOWS);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -9541,8 +9646,7 @@
 
     @Override
     public void positionTaskInStack(int taskId, int stackId, int position) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "positionTaskInStack()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
         if (stackId == HOME_STACK_ID) {
             throw new IllegalArgumentException(
                     "positionTaskInStack: Attempt to change the position of task "
@@ -9563,8 +9667,7 @@
 
     @Override
     public List<StackInfo> getAllStackInfos() {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "getAllStackInfos()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -9577,8 +9680,7 @@
 
     @Override
     public StackInfo getStackInfo(int stackId) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "getStackInfo()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -9591,8 +9693,7 @@
 
     @Override
     public boolean isInHomeStack(int taskId) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "getStackInfo()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -9706,8 +9807,7 @@
 
     @Override
     public void startLockTaskModeOnCurrent() throws RemoteException {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "startLockTaskModeOnCurrent");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startLockTaskModeOnCurrent");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -9759,8 +9859,7 @@
 
     @Override
     public void stopLockTaskModeOnCurrent() throws RemoteException {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "stopLockTaskModeOnCurrent");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopLockTaskModeOnCurrent");
         long ident = Binder.clearCallingIdentity();
         try {
             stopLockTaskMode();
@@ -9802,7 +9901,7 @@
             ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager()
                     .queryContentProviders(app.processName, app.uid,
                             STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
-                                    | PackageManager.MATCH_ENCRYPTION_DEFAULT);
+                                    | MATCH_DEBUG_TRIAGED_MISSING);
             providers = slice != null ? slice.getList() : null;
         } catch (RemoteException ex) {
         }
@@ -10747,6 +10846,49 @@
     }
 
     /**
+     * When a user is unlocked, we need to install encryption-unaware providers
+     * belonging to any running apps.
+     */
+    private void installEncryptionUnawareProviders(int userId) {
+        if (!StorageManager.isFileBasedEncryptionEnabled()) {
+            // TODO: eventually pivot this back to look at current user state,
+            // similar to the comment in UserManager.isUserUnlocked(), but for
+            // now, if we started apps when "unlocked" then unaware providers
+            // have already been spun up.
+            return;
+        }
+
+        synchronized (this) {
+            final int NP = mProcessNames.getMap().size();
+            for (int ip = 0; ip < NP; ip++) {
+                final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                final int NA = apps.size();
+                for (int ia = 0; ia < NA; ia++) {
+                    final ProcessRecord app = apps.valueAt(ia);
+                    if (app.userId != userId || app.thread == null) continue;
+
+                    final int NG = app.pkgList.size();
+                    for (int ig = 0; ig < NG; ig++) {
+                        try {
+                            final String pkgName = app.pkgList.keyAt(ig);
+                            final PackageInfo pkgInfo = AppGlobals.getPackageManager()
+                                    .getPackageInfo(pkgName,
+                                            GET_PROVIDERS | MATCH_ENCRYPTION_UNAWARE, userId);
+                            if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
+                                for (ProviderInfo provInfo : pkgInfo.providers) {
+                                    Log.v(TAG, "Installing " + provInfo);
+                                    app.thread.scheduleInstallProvider(provInfo);
+                                }
+                            }
+                        } catch (RemoteException ignored) {
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Allows apps to retrieve the MIME type of a URI.
      * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
      * users, then it does not need permission to access the ContentProvider.
@@ -11018,11 +11160,7 @@
 
     /** Pokes the task persister. */
     void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
-        if (task != null && task.stack != null && task.stack.isHomeStack()) {
-            // Never persist the home stack.
-            return;
-        }
-        mTaskPersister.wakeup(task, flush);
+        mRecentTasks.notifyTaskPersisterLocked(task, flush);
     }
 
     /** Notifies all listeners when the task stack has changed. */
@@ -11033,6 +11171,12 @@
         mHandler.sendMessageDelayed(nmsg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
     }
 
+    /** Notifies all listeners when an Activity is pinned. */
+    void notifyActivityPinnedLocked() {
+        mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
+        mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG).sendToTarget();
+    }
+
     @Override
     public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
         mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
@@ -11319,8 +11463,23 @@
         }
     }
 
-    public void requestBugReport(boolean progress) {
-        final String service = progress ? "bugreportplus" : "bugreport";
+    public void requestBugReport(int bugreportType) {
+        String service = null;
+        switch (bugreportType) {
+            case ActivityManager.BUGREPORT_OPTION_FULL:
+                service = "bugreport";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
+                service = "bugreportplus";
+                break;
+            case ActivityManager.BUGREPORT_OPTION_REMOTE:
+                service = "bugreportremote";
+                break;
+        }
+        if (service == null) {
+            throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
+                    + bugreportType);
+        }
         enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
         SystemProperties.set("ctl.start", service);
     }
@@ -12122,6 +12281,8 @@
                             sb.append(proc.processName);
                             sb.append(" in idle maint: pss=");
                             sb.append(proc.lastPss);
+                            sb.append(", swapPss=");
+                            sb.append(proc.lastSwapPss);
                             sb.append(", initialPss=");
                             sb.append(proc.initialIdlePss);
                             sb.append(", period=");
@@ -12149,7 +12310,9 @@
     private void retrieveSettings() {
         final ContentResolver resolver = mContext.getContentResolver();
         final boolean freeformWindowManagement =
-                mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+                mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                        || Settings.Global.getInt(
+                                resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
         final boolean supportsPictureInPicture =
                 mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
 
@@ -12158,9 +12321,8 @@
         final boolean alwaysFinishActivities =
                 Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
         final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
-        final int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
         final boolean forceResizable = Settings.Global.getInt(
-                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, defaultForceResizable) != 0;
+                resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
         // Transfer any global setting for forcing RTL layout, into a System Property
         SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
 
@@ -12193,6 +12355,17 @@
                     com.android.internal.R.dimen.thumbnail_height);
             mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
                     com.android.internal.R.string.config_defaultPictureInPictureBounds));
+            final String appsNotReportingCrashes = res.getString(
+                    com.android.internal.R.string.config_appsNotReportingCrashes);
+            if (appsNotReportingCrashes != null) {
+                final String[] split = appsNotReportingCrashes.split(",");
+                if (split.length > 0) {
+                    mAppsNotReportingCrashes = new ArraySet<>();
+                    for (int i = 0; i < split.length; i++) {
+                        mAppsNotReportingCrashes.add(split[i]);
+                    }
+                }
+            }
         }
     }
 
@@ -12343,19 +12516,13 @@
         List<ResolveInfo> ris = null;
         try {
             ris = AppGlobals.getPackageManager().queryIntentReceivers(
-                    intent, null, 0, UserHandle.USER_SYSTEM);
+                    intent, null, MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
         } catch (RemoteException e) {
         }
         if (ris == null) {
             return false;
         }
-        for (int i=ris.size()-1; i>=0; i--) {
-            if ((ris.get(i).activityInfo.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                ris.remove(i);
-            }
-        }
-        intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+        intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING);
 
         ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
         for (int i=0; i<ris.size(); i++) {
@@ -12406,11 +12573,7 @@
             // security checks.
             mUserController.updateCurrentProfileIdsLocked();
 
-            mRecentTasks.clear();
-            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(mUserController.getUserIds()));
-            mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
-            mTaskPersister.startPersisting();
-
+            mRecentTasks.onSystemReady();
             // Check to see if there are any update receivers to run.
             if (!mDidUpdate) {
                 if (mWaitingUpdate) {
@@ -12517,7 +12680,6 @@
 
         if (goingCallback != null) goingCallback.run();
 
-
         mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
                 Integer.toString(currentUserId), currentUserId);
         mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
@@ -12603,7 +12765,7 @@
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-            mStackSupervisor.resumeTopActivitiesLocked();
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
             mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
         }
     }
@@ -12714,10 +12876,10 @@
                 // annoy the user repeatedly.  Unless it is persistent, since those
                 // processes run critical code.
                 removeProcessLocked(app, false, false, "crash");
-                mStackSupervisor.resumeTopActivitiesLocked();
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 return false;
             }
-            mStackSupervisor.resumeTopActivitiesLocked();
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
         } else {
             mStackSupervisor.finishTopRunningActivityLocked(app, reason);
         }
@@ -13875,7 +14037,7 @@
         if (dumpPackage != null) {
             IPackageManager pm = AppGlobals.getPackageManager();
             try {
-                dumpUid = pm.getPackageUid(dumpPackage, 0);
+                dumpUid = pm.getPackageUid(dumpPackage, MATCH_UNINSTALLED_PACKAGES, 0);
             } catch (RemoteException e) {
             }
         }
@@ -14811,7 +14973,8 @@
             int dumpUid = -2;
             if (dumpPackage != null) {
                 try {
-                    dumpUid = mContext.getPackageManager().getPackageUid(dumpPackage, 0);
+                    dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
+                            MATCH_UNINSTALLED_PACKAGES, 0);
                 } catch (NameNotFoundException e) {
                     dumpUid = -1;
                 }
@@ -15022,6 +15185,7 @@
                 pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
                 pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
                 pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
+                pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
                 pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024);
                 pw.println();
                 pw.print(prefix);
@@ -15175,32 +15339,35 @@
         final String label;
         final String shortLabel;
         final long pss;
+        final long swapPss;
         final int id;
         final boolean hasActivities;
         ArrayList<MemItem> subitems;
 
-        public MemItem(String _label, String _shortLabel, long _pss, int _id,
+        public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id,
                 boolean _hasActivities) {
             isProc = true;
             label = _label;
             shortLabel = _shortLabel;
             pss = _pss;
+            swapPss = _swapPss;
             id = _id;
             hasActivities = _hasActivities;
         }
 
-        public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+        public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id) {
             isProc = false;
             label = _label;
             shortLabel = _shortLabel;
             pss = _pss;
+            swapPss = _swapPss;
             id = _id;
             hasActivities = false;
         }
     }
 
     static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
-            ArrayList<MemItem> items, boolean sort, boolean isCompact) {
+            ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpSwapPss) {
         if (sort && !isCompact) {
             Collections.sort(items, new Comparator<MemItem>() {
                 @Override
@@ -15218,18 +15385,24 @@
         for (int i=0; i<items.size(); i++) {
             MemItem mi = items.get(i);
             if (!isCompact) {
-                pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
+                if (dumpSwapPss) {
+                    pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss),
+                            mi.label, stringifyKBSize(mi.swapPss));
+                } else {
+                    pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
+                }
             } else if (mi.isProc) {
                 pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
-                pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss);
+                pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); pw.print(",");
+                pw.print(dumpSwapPss ? mi.swapPss : "N/A");
                 pw.println(mi.hasActivities ? ",a" : ",e");
             } else {
                 pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(",");
-                pw.println(mi.pss);
+                pw.println(mi.pss); pw.print(dumpSwapPss ? mi.swapPss : "N/A");
             }
             if (mi.subitems != null) {
-                dumpMemItems(pw, prefix + "           ", mi.shortLabel, mi.subitems,
-                        true, isCompact);
+                dumpMemItems(pw, prefix + "    ", mi.shortLabel, mi.subitems,
+                        true, isCompact, dumpSwapPss);
             }
         }
     }
@@ -15358,6 +15531,8 @@
         boolean isCompact = false;
         boolean localOnly = false;
         boolean packages = false;
+        boolean isCheckinRequest = false;
+        boolean dumpSwapPss = false;
 
         int opti = 0;
         while (opti < args.length) {
@@ -15370,6 +15545,7 @@
                 dumpDetails = true;
                 dumpFullDetails = true;
                 dumpDalvik = true;
+                dumpSwapPss = true;
             } else if ("-d".equals(opt)) {
                 dumpDalvik = true;
             } else if ("-c".equals(opt)) {
@@ -15377,22 +15553,29 @@
             } else if ("-s".equals(opt)) {
                 dumpDetails = true;
                 dumpSummaryOnly = true;
+            } else if ("-S".equals(opt)) {
+                dumpSwapPss = true;
             } else if ("--oom".equals(opt)) {
                 oomOnly = true;
             } else if ("--local".equals(opt)) {
                 localOnly = true;
             } else if ("--package".equals(opt)) {
                 packages = true;
+            } else if ("--checkin".equals(opt)) {
+                isCheckinRequest = true;
+
             } else if ("-h".equals(opt)) {
                 pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]");
                 pw.println("  -a: include all available information for each process.");
                 pw.println("  -d: include dalvik details.");
                 pw.println("  -c: dump in a compact machine-parseable representation.");
                 pw.println("  -s: dump only summary of application memory usage.");
+                pw.println("  -S: dump also SwapPss.");
                 pw.println("  --oom: only show processes organized by oom adj.");
                 pw.println("  --local: only collect details locally, don't call process.");
                 pw.println("  --package: interpret process arg as package, dumping all");
                 pw.println("             processes that have loaded that package.");
+                pw.println("  --checkin: dump data for a checkin");
                 pw.println("If [process] is specified it can be the name or ");
                 pw.println("pid of a specific process to dump.");
                 return;
@@ -15401,7 +15584,6 @@
             }
         }
 
-        final boolean isCheckinRequest = scanArgs(args, "--checkin");
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
         final long[] tmpLong = new long[1];
@@ -15473,18 +15655,28 @@
         ArrayList<MemItem> procMems = new ArrayList<MemItem>();
         final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
         long nativePss = 0;
+        long nativeSwapPss = 0;
         long dalvikPss = 0;
+        long dalvikSwapPss = 0;
         long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
                 EmptyArray.LONG;
+        long[] dalvikSubitemSwapPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+                EmptyArray.LONG;
         long otherPss = 0;
+        long otherSwapPss = 0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+        long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
 
         long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
+        long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
         ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
                 new ArrayList[DUMP_MEM_OOM_LABEL.length];
 
         long totalPss = 0;
+        long totalSwapPss = 0;
         long cachedPss = 0;
+        long cachedSwapPss = 0;
+        boolean hasSwapPss = false;
 
         Debug.MemoryInfo mi = null;
         for (int i = procs.size() - 1 ; i >= 0 ; i--) {
@@ -15508,6 +15700,7 @@
                 }
                 if (dumpDetails || (!brief && !oomOnly)) {
                     Debug.getMemoryInfo(pid, mi);
+                    hasSwapPss = mi.hasSwappedOutPss;
                 } else {
                     mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
                     mi.dalvikPrivateDirty = (int)tmpLong[0];
@@ -15535,6 +15728,7 @@
 
                 final long myTotalPss = mi.getTotalPss();
                 final long myTotalUss = mi.getTotalUss();
+                final long myTotalSwapPss = mi.getTotalSwappedOutPss();
 
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
@@ -15545,32 +15739,43 @@
 
                 if (!isCheckinRequest && mi != null) {
                     totalPss += myTotalPss;
+                    totalSwapPss += myTotalSwapPss;
                     MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
-                            (hasActivities ? " / activities)" : ")"),
-                            r.processName, myTotalPss, pid, hasActivities);
+                            (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
+                            myTotalSwapPss, pid, hasActivities);
                     procMems.add(pssItem);
                     procMemsMap.put(pid, pssItem);
 
                     nativePss += mi.nativePss;
+                    nativeSwapPss += mi.nativeSwappedOutPss;
                     dalvikPss += mi.dalvikPss;
+                    dalvikSwapPss += mi.dalvikSwappedOutPss;
                     for (int j=0; j<dalvikSubitemPss.length; j++) {
                         dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        dalvikSubitemSwapPss[j] +=
+                                mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                     }
                     otherPss += mi.otherPss;
+                    otherSwapPss += mi.otherSwappedOutPss;
                     for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                         long mem = mi.getOtherPss(j);
                         miscPss[j] += mem;
                         otherPss -= mem;
+                        mem = mi.getOtherSwappedOutPss(j);
+                        miscSwapPss[j] += mem;
+                        otherSwapPss -= mem;
                     }
 
                     if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
                         cachedPss += myTotalPss;
+                        cachedSwapPss += myTotalSwapPss;
                     }
 
                     for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
                         if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
                                 || oomIndex == (oomPss.length-1)) {
                             oomPss[oomIndex] += myTotalPss;
+                            oomSwapPss[oomIndex] += myTotalSwapPss;
                             if (oomProcs[oomIndex] == null) {
                                 oomProcs[oomIndex] = new ArrayList<MemItem>();
                             }
@@ -15605,26 +15810,35 @@
                         }
 
                         final long myTotalPss = mi.getTotalPss();
+                        final long myTotalSwapPss = mi.getTotalSwappedOutPss();
                         totalPss += myTotalPss;
                         nativeProcTotalPss += myTotalPss;
 
                         MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
-                                st.name, myTotalPss, st.pid, false);
+                                st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false);
                         procMems.add(pssItem);
 
                         nativePss += mi.nativePss;
+                        nativeSwapPss += mi.nativeSwappedOutPss;
                         dalvikPss += mi.dalvikPss;
+                        dalvikSwapPss += mi.dalvikSwappedOutPss;
                         for (int j=0; j<dalvikSubitemPss.length; j++) {
-                            dalvikSubitemPss[j] += mi.getOtherPss(
-                                    Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                            dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                            dalvikSubitemSwapPss[j] +=
+                                    mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
                         }
                         otherPss += mi.otherPss;
+                        otherSwapPss += mi.otherSwappedOutPss;
                         for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                             long mem = mi.getOtherPss(j);
                             miscPss[j] += mem;
                             otherPss -= mem;
+                            mem = mi.getOtherSwappedOutPss(j);
+                            miscSwapPss[j] += mem;
+                            otherSwapPss -= mem;
                         }
                         oomPss[0] += myTotalPss;
+                        oomSwapPss[0] += myTotalSwapPss;
                         if (oomProcs[0] == null) {
                             oomProcs[0] = new ArrayList<MemItem>();
                         }
@@ -15635,21 +15849,23 @@
 
             ArrayList<MemItem> catMems = new ArrayList<MemItem>();
 
-            catMems.add(new MemItem("Native", "Native", nativePss, -1));
-            final MemItem dalvikItem = new MemItem("Dalvik", "Dalvik", dalvikPss, -2);
+            catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1));
+            final MemItem dalvikItem =
+                    new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, -2);
             if (dalvikSubitemPss.length > 0) {
                 dalvikItem.subitems = new ArrayList<MemItem>();
                 for (int j=0; j<dalvikSubitemPss.length; j++) {
                     final String name = Debug.MemoryInfo.getOtherLabel(
                             Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                    dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], j));
+                    dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
+                                    dalvikSubitemSwapPss[j], j));
                 }
             }
             catMems.add(dalvikItem);
-            catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3));
+            catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3));
             for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                 String label = Debug.MemoryInfo.getOtherLabel(j);
-                catMems.add(new MemItem(label, label, miscPss[j], j));
+                catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j));
             }
 
             ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
@@ -15657,30 +15873,31 @@
                 if (oomPss[j] != 0) {
                     String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
                             : DUMP_MEM_OOM_LABEL[j];
-                    MemItem item = new MemItem(label, label, oomPss[j],
+                    MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j],
                             DUMP_MEM_OOM_ADJ[j]);
                     item.subitems = oomProcs[j];
                     oomMems.add(item);
                 }
             }
 
+            dumpSwapPss = dumpSwapPss && hasSwapPss && totalSwapPss != 0;
             if (!brief && !oomOnly && !isCompact) {
                 pw.println();
                 pw.println("Total PSS by process:");
-                dumpMemItems(pw, "  ", "proc", procMems, true, isCompact);
+                dumpMemItems(pw, "  ", "proc", procMems, true, isCompact, dumpSwapPss);
                 pw.println();
             }
             if (!isCompact) {
                 pw.println("Total PSS by OOM adjustment:");
             }
-            dumpMemItems(pw, "  ", "oom", oomMems, false, isCompact);
+            dumpMemItems(pw, "  ", "oom", oomMems, false, isCompact, dumpSwapPss);
             if (!brief && !oomOnly) {
                 PrintWriter out = categoryPw != null ? categoryPw : pw;
                 if (!isCompact) {
                     out.println();
                     out.println("Total PSS by category:");
                 }
-                dumpMemItems(out, "  ", "cat", catMems, true, isCompact);
+                dumpMemItems(out, "  ", "cat", catMems, true, isCompact, dumpSwapPss);
             }
             if (!isCompact) {
                 pw.println();
@@ -15738,18 +15955,17 @@
                     pw.println(totalPss - cachedPss);
                 }
             }
+            long lostRAM = memInfo.getTotalSizeKb()
+                    - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+                    - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
             if (!isCompact) {
                 pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
                         + memInfo.getKernelUsedSizeKb())); pw.print(" (");
                 pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + ");
                 pw.print(stringifyKBSize(memInfo.getKernelUsedSizeKb())); pw.print(" kernel)\n");
-                pw.print(" Lost RAM: "); pw.println(stringifyKBSize(memInfo.getTotalSizeKb()
-                        - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
-                        - memInfo.getKernelUsedSizeKb()));
+                pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM));
             } else {
-                pw.print("lostram,"); pw.println(memInfo.getTotalSizeKb()
-                        - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
-                        - memInfo.getKernelUsedSizeKb());
+                pw.print("lostram,"); pw.println(lostRAM);
             }
             if (!brief) {
                 if (memInfo.getZramTotalSizeKb() != 0) {
@@ -16047,7 +16263,7 @@
         memInfoBuilder.append("  Lost RAM: ");
         memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
                 - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
-                - memInfo.getKernelUsedSizeKb()));
+                - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb()));
         memInfoBuilder.append("\n");
         Slog.i(TAG, "Low on memory:");
         Slog.i(TAG, shortNativeBuilder.toString());
@@ -17010,7 +17226,7 @@
     private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
             int callingUid, int[] users) {
         // TODO: come back and remove this assumption to triage all broadcasts
-        int pmFlags = STOCK_PM_FLAGS | PackageManager.MATCH_ENCRYPTION_DEFAULT;
+        int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
 
         List<ResolveInfo> receivers = null;
         try {
@@ -17780,6 +17996,11 @@
                         "Unable to find instrumentation target package: " + ii.targetPackage);
                 return false;
             }
+            if (!ai.hasCode()) {
+                reportStartInstrumentationFailure(watcher, className,
+                        "Instrumentation target has no code: " + ii.targetPackage);
+                return false;
+            }
 
             int match = mContext.getPackageManager().checkSignatures(
                     ii.targetPackage, ii.packageName);
@@ -17933,17 +18154,15 @@
 
     @Override
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "suppressResizeConfigChanges()");
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
         synchronized (this) {
             mSuppressResizeConfigChanges = suppress;
         }
     }
 
     @Override
-    public void moveTasksToFullscreenStack(int fromStackId) {
-        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "moveTasksToFullscreenStack()");
+    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()");
         if (fromStackId == HOME_STACK_ID) {
             throw new IllegalArgumentException("You can't move tasks from the home stack.");
         }
@@ -17952,9 +18171,18 @@
             final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
             if (stack != null) {
                 final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                for (int i = tasks.size() - 1; i >= 0; i--) {
-                    mStackSupervisor.positionTaskInStackLocked(tasks.get(i).taskId,
-                            FULLSCREEN_WORKSPACE_STACK_ID, 0);
+                final int size = tasks.size();
+                if (onTop) {
+                    for (int i = 0; i < size; i++) {
+                        mStackSupervisor.moveTaskToStackLocked(tasks.get(i).taskId,
+                                FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, !FORCE_FOCUS,
+                                "moveTasksToFullscreenStack", ANIMATE);
+                    }
+                } else {
+                    for (int i = size - 1; i >= 0; i--) {
+                        mStackSupervisor.positionTaskInStackLocked(tasks.get(i).taskId,
+                                FULLSCREEN_WORKSPACE_STACK_ID, 0);
+                    }
                 }
             }
             Binder.restoreCallingIdentity(origId);
@@ -18062,10 +18290,18 @@
                 EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
 
                 if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
-                    if (mSupportedSystemLocales == null) {
-                        mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+                    final Locale locale;
+                    if (values.getLocales().size() == 1) {
+                        // This is an optimization to avoid the JNI call when the result of
+                        // getFirstMatch() does not depend on the supported locales.
+                        locale = values.getLocales().getPrimary();
+                    } else {
+                        if (mSupportedSystemLocales == null) {
+                            mSupportedSystemLocales =
+                                    Resources.getSystem().getAssets().getLocales();
+                        }
+                        locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
                     }
-                    final Locale locale = values.getLocales().getFirstMatch(mSupportedSystemLocales);
                     SystemProperties.set("persist.sys.locale", locale.toLanguageTag());
                     mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
                             locale));
@@ -18176,9 +18412,12 @@
      * dialog / global actions also might want different behaviors.
      */
     private static final boolean shouldShowDialogs(Configuration config) {
-        return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
-                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
-                && config.navigation == Configuration.NAVIGATION_NONAV);
+        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+                                   && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+                                   && config.navigation == Configuration.NAVIGATION_NONAV);
+        final boolean uiIsNotCarType = !((config.uiMode & Configuration.UI_MODE_TYPE_MASK)
+                                    == Configuration.UI_MODE_TYPE_CAR);
+        return inputMethodExists && uiIsNotCarType;
     }
 
     @Override
@@ -18990,8 +19229,10 @@
     /**
      * Record new PSS sample for a process.
      */
-    void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long now) {
-        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024);
+    void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss,
+            long now) {
+        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
+                swapPss * 1024);
         proc.lastPssTime = now;
         proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
         if (DEBUG_PSS) Slog.d(TAG_PSS,
@@ -19001,8 +19242,10 @@
             proc.initialIdlePss = pss;
         }
         proc.lastPss = pss;
+        proc.lastSwapPss = swapPss;
         if (procState >= ActivityManager.PROCESS_STATE_HOME) {
             proc.lastCachedPss = pss;
+            proc.lastCachedSwapPss = swapPss;
         }
 
         final SparseArray<Pair<Long, String>> watchUids
@@ -19438,7 +19681,7 @@
                 // states, which well tend to give noisy data.
                 long start = SystemClock.uptimeMillis();
                 long pss = Debug.getPss(app.pid, mTmpLong, null);
-                recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], now);
+                recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], now);
                 mPendingPssProcesses.remove(app);
                 Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
                         + " to " + app.curProcState + ": "
@@ -20590,10 +20833,6 @@
         return mUserController.stopUser(userId, force, callback);
     }
 
-    void onUserRemovedLocked(int userId) {
-        mRecentTasks.removeTasksForUserLocked(userId);
-    }
-
     @Override
     public UserInfo getCurrentUser() {
         return mUserController.getCurrentUser();
@@ -20648,6 +20887,10 @@
         return newInfo;
     }
 
+    public boolean isUserStopped(int userId) {
+        return mUserController.getStartedUserStateLocked(userId) == null;
+    }
+
     ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
         if (aInfo == null
                 || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) {
@@ -20800,7 +21043,7 @@
         @Override
         public void onUserRemoved(int userId) {
             synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.onUserRemovedLocked(userId);
+                ActivityManagerService.this.onUserStoppedLocked(userId);
             }
         }
     }
@@ -20889,7 +21132,7 @@
         public void moveToFront() {
             checkCaller();
             // Will bring task to front if it already has a root activity.
-            startActivityFromRecentsInner(mTaskId, INVALID_STACK_ID, null);
+            startActivityFromRecentsInner(mTaskId, null);
         }
 
         @Override
@@ -20954,7 +21197,7 @@
         IPackageManager pm = AppGlobals.getPackageManager();
         int pkgUid = -1;
         try {
-            pkgUid = pm.getPackageUid(packageName, userId);
+            pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
         } catch (RemoteException e) {
         }
         if (pkgUid == -1) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 13c1417..8c99739 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -21,6 +21,8 @@
 import android.os.ShellCommand;
 import android.os.UserHandle;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.PrintWriter;
 
 class ActivityManagerShellCommand extends ShellCommand {
@@ -58,6 +60,8 @@
                     return runTrackAssociations(pw);
                 case "untrack-associations":
                     return runUntrackAssociations(pw);
+                case "is-user-stopped":
+                    return runIsUserStopped(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -67,6 +71,13 @@
         return -1;
     }
 
+    int runIsUserStopped(PrintWriter pw) {
+        int userId = UserHandle.parseUserArg(getNextArgRequired());
+        boolean stopped = mInternal.isUserStopped(userId);
+        pw.println(stopped);
+        return 0;
+    }
+
     int runForceStop(PrintWriter pw) throws RemoteException {
         int userId = UserHandle.USER_ALL;
 
@@ -107,7 +118,7 @@
     int runWrite(PrintWriter pw) {
         mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerUidObserver()");
-        mInternal.mTaskPersister.flush();
+        mInternal.mRecentTasks.flush();
         pw.println("All tasks persisted.");
         return 0;
     }
@@ -190,6 +201,8 @@
             pw.println("    Enable association tracking.");
             pw.println("  untrack-associations");
             pw.println("    Disable and clear association tracking.");
+            pw.println("  is-user-stopped <USER_ID>");
+            pw.println("    returns whether <USER_ID> has been stopped or not");
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 9680382..ffb2fc4 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -5,6 +5,7 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 
 import android.app.ActivityManager.StackId;
 import android.content.Context;
@@ -26,7 +27,7 @@
     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
     // time we log.
     private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
-            "tron_varz_window_time_0", "tron_varz_window_time_1", "tron_varz_window_time_2"};
+            "window_time_0", "window_time_1", "window_time_2"};
 
     private int mWindowState = WINDOW_STATE_STANDARD;
     private long mLastLogTimeSecs;
@@ -51,7 +52,7 @@
         mLastLogTimeSecs = now;
 
         ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
-        if (stack != null && stack.isStackVisibleLocked()) {
+        if (stack != null && stack.getStackVisibilityLocked() != STACK_INVISIBLE) {
             mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
             return;
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4d9120b..b16bd2b 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,7 +16,11 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE;
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
@@ -167,7 +171,9 @@
     boolean stopped;        // is activity pause finished?
     boolean delayedResume;  // not yet resumed because of stopped app switches?
     boolean finishing;      // activity in pending finish list?
-    boolean configDestroy;  // need to destroy due to config change?
+    boolean deferRelaunchUntilPaused;   // relaunch of activity is being deferred until pause is
+                                        // completed
+    boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch
     int configChangeFlags;  // which config values have changed
     boolean keysPaused;     // has key dispatching been paused for it?
     int launchMode;         // the launch mode activity attribute.
@@ -180,9 +186,9 @@
     boolean immersive;      // immersive mode (don't interrupt if possible)
     boolean forceNewConfig; // force re-create with new config next time
     int launchCount;        // count of launches since last state
-    long lastLaunchTime;    // time of last lauch of this activity
+    long lastLaunchTime;    // time of last launch of this activity
     boolean isVrActivity;   // is the activity running in VR mode?
-    ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>();
+    ArrayList<ActivityContainer> mChildContainers = new ArrayList<>();
 
     String stringName;      // for caching of toString().
 
@@ -337,8 +343,8 @@
                     else TimeUtils.formatDuration(lastVisibleTime, now, pw);
                     pw.println();
         }
-        if (configDestroy || configChangeFlags != 0) {
-            pw.print(prefix); pw.print("configDestroy="); pw.print(configDestroy);
+        if (deferRelaunchUntilPaused || configChangeFlags != 0) {
+            pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused);
                     pw.print(" configChangeFlags=");
                     pw.println(Integer.toHexString(configChangeFlags));
         }
@@ -547,7 +553,7 @@
         stopped = false;
         delayedResume = false;
         finishing = false;
-        configDestroy = false;
+        deferRelaunchUntilPaused = false;
         keysPaused = false;
         inHistory = false;
         visible = false;
@@ -739,6 +745,22 @@
                         (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
     }
 
+    boolean isFocusable() {
+        return StackId.canReceiveKeys(task.stack.mStackId) || isAlwaysFocusable();
+    }
+
+    boolean isResizeable() {
+        return (info.flags & FLAG_RESIZEABLE) != 0;
+    }
+
+    boolean supportsPictureInPicture() {
+        return (info.flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
+    }
+
+    boolean isAlwaysFocusable() {
+        return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
+    }
+
     void makeFinishingLocked() {
         if (!finishing) {
             if (task != null && task.stack != null
@@ -1246,7 +1268,7 @@
             final String iconFilename = createImageFilename(createTime, task.taskId);
             final File iconFile = new File(TaskPersister.getUserImagesDir(userId), iconFilename);
             final String iconFilePath = iconFile.getAbsolutePath();
-            mStackSupervisor.mService.mTaskPersister.saveImage(icon, iconFilePath);
+            service.mRecentTasks.saveImage(icon, iconFilePath);
             _taskDescription.setIconFilename(iconFilePath);
         }
         taskDescription = _taskDescription;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1492e23..e3f4999 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -21,8 +21,13 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
 
+import static android.content.res.Configuration.SCREENLAYOUT_UNDEFINED;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityManagerService.LOCK_SCREEN_SHOWN;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -161,6 +166,14 @@
         DESTROYED
     }
 
+    // Stack is not considered visible.
+    static final int STACK_INVISIBLE = 0;
+    // Stack is considered visible
+    static final int STACK_VISIBLE = 1;
+    // Stack is considered visible, but only becuase it has activity that is visible behind other
+    // activities and there is a specific combination of stacks.
+    static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
+
     final ActivityManagerService mService;
     final WindowManagerService mWindowManager;
     private final RecentTasks mRecentTasks;
@@ -560,7 +573,7 @@
 
         // TODO(multi-display): Needs to also work if focus is moving to the non-home display.
         if (isOnHomeDisplay()) {
-            mStackSupervisor.setFocusStack(reason, this);
+            mStackSupervisor.setFocusStackUnchecked(reason, this);
         }
         if (task != null) {
             insertTaskAtTop(task, null);
@@ -572,6 +585,16 @@
         }
     }
 
+    boolean isFocusable() {
+        if (StackId.canReceiveKeys(mStackId)) {
+            return true;
+        }
+        // The stack isn't focusable. See if its top activity is focusable to force focus on the
+        // stack.
+        final ActivityRecord r = topRunningActivityLocked();
+        return r != null && r.isFocusable();
+    }
+
     final boolean isAttached() {
         return mStacks != null;
     }
@@ -917,7 +940,7 @@
         if (prev == null) {
             if (!resuming) {
                 Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mStackSupervisor.resumeTopActivitiesLocked();
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
             }
             return false;
         }
@@ -1008,7 +1031,7 @@
             // pause, so just treat it as being paused now.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
             if (!resuming) {
-                mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
             }
             return false;
         }
@@ -1073,9 +1096,9 @@
             if (r.finishing) {
                 r.clearOptionsLocked();
             } else {
-                if (r.configDestroy) {
+                if (r.deferRelaunchUntilPaused) {
                     destroyActivityLocked(r, true, "stop-config");
-                    mStackSupervisor.resumeTopActivitiesLocked();
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 } else {
                     mStackSupervisor.updatePreviousProcessLocked(r);
                 }
@@ -1099,14 +1122,11 @@
                     if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
                             "Complete pause, no longer waiting: " + prev);
                 }
-                if (prev.configDestroy) {
-                    // The previous is being paused because the configuration
-                    // is changing, which means it is actually stopping...
-                    // To juggle the fact that we are also starting a new
-                    // instance right now, we need to first completely stop
-                    // the current instance before starting the new one.
-                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Destroying after pause: " + prev);
-                    destroyActivityLocked(prev, true, "pause-config");
+                if (prev.deferRelaunchUntilPaused) {
+                    // Complete the deferred relaunch that was waiting for pause to complete.
+                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
+                    relaunchActivityLocked(prev, prev.configChangeFlags, false,
+                            prev.preserveWindowOnDeferredRelaunch);
                 } else if (wasStopping) {
                     // We are also stopping, the stop request must have gone soon after the pause.
                     // We can't clobber it, because the stop confirmation will not be handled.
@@ -1131,17 +1151,16 @@
         if (resumeNext) {
             final ActivityStack topStack = mStackSupervisor.getFocusedStack();
             if (!mService.isSleepingOrShuttingDown()) {
-                mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
+                mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
             } else {
                 mStackSupervisor.checkReadyForSleepLocked();
                 ActivityRecord top = topStack.topRunningActivityLocked();
                 if (top == null || (prev != null && top != prev)) {
-                    // If there are no more activities available to run,
-                    // do resume anyway to start something.  Also if the top
-                    // activity on the stack is not the just paused activity,
-                    // we need to go ahead and resume it to ensure we complete
-                    // an in-flight app switch.
-                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+                    // If there are no more activities available to run, do resume anyway to start
+                    // something. Also if the top activity on the stack is not the just paused
+                    // activity, we need to go ahead and resume it to ensure we complete an
+                    // in-flight app switch.
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 }
             }
         }
@@ -1168,8 +1187,12 @@
             prev.cpuTimeAtResume = 0; // reset it
         }
 
-        // Notfiy when the task stack has changed
-        mService.notifyTaskStackChangedLocked();
+        // Notify when the task stack has changed, but only if visibilities changed (not just
+        // focus).
+        if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause) {
+            mService.notifyTaskStackChangedLocked();
+            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+        }
     }
 
     private void addToStopping(ActivityRecord r) {
@@ -1246,6 +1269,7 @@
             ActivityContainer container = containers.get(containerNdx);
             container.setVisible(visible);
         }
+        mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
     }
 
     // Find the first visible activity above the passed activity and if it is translucent return it
@@ -1296,7 +1320,7 @@
         return null;
     }
 
-    private ActivityStack getNextVisibleStackLocked() {
+    ActivityStack getNextFocusableStackLocked() {
         ArrayList<ActivityStack> stacks = mStacks;
         final ActivityRecord parent = mActivityContainer.mParentActivity;
         if (parent != null) {
@@ -1305,7 +1329,8 @@
         if (stacks != null) {
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 ActivityStack stack = stacks.get(i);
-                if (stack != this && stack.isStackVisibleLocked()) {
+                if (stack != this && stack.isFocusable()
+                        && stack.getStackVisibilityLocked() != STACK_INVISIBLE) {
                     return stack;
                 }
             }
@@ -1347,14 +1372,17 @@
         return true;
     }
 
-    /** Returns true if the stack is considered visible. */
-    boolean isStackVisibleLocked() {
+    /**
+     * Returns stack's visibility: {@link #STACK_INVISIBLE}, {@link #STACK_VISIBLE} or
+     * {@link #STACK_VISIBLE_ACTIVITY_BEHIND}.
+     */
+    int getStackVisibilityLocked() {
         if (!isAttached()) {
-            return false;
+            return STACK_INVISIBLE;
         }
 
         if (mStackSupervisor.isFrontStack(this) || mStackSupervisor.isFocusedStack(this)) {
-            return true;
+            return STACK_VISIBLE;
         }
 
         final int stackIndex = mStacks.indexOf(this);
@@ -1362,32 +1390,34 @@
         if (stackIndex == mStacks.size() - 1) {
             Slog.wtf(TAG,
                     "Stack=" + this + " isn't front stack but is at the top of the stack list");
-            return false;
+            return STACK_INVISIBLE;
         }
 
         final boolean isLockscreenShown = mService.mLockScreenShown == LOCK_SCREEN_SHOWN;
         if (isLockscreenShown && !StackId.isAllowedOverLockscreen(mStackId)) {
-            return false;
+            return STACK_INVISIBLE;
         }
 
         final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
         final int focusedStackId = focusedStack.mStackId;
 
         if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID
-                && hasVisibleBehindActivity() && focusedStackId == HOME_STACK_ID) {
+                && hasVisibleBehindActivity() && focusedStackId == HOME_STACK_ID
+                && !focusedStack.topActivity().fullscreen) {
             // The fullscreen stack should be visible if it has a visible behind activity behind
-            // the home stack that will be translucent.
-            return true;
+            // the home stack that is translucent.
+            return STACK_VISIBLE_ACTIVITY_BEHIND;
         }
 
         if (mStackId == DOCKED_STACK_ID) {
             // Docked stack is always visible, except in the case where the home activity
             // is the top running activity in the focused home stack.
             if (focusedStackId != HOME_STACK_ID) {
-                return true;
+                return STACK_VISIBLE;
             }
             ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked();
-            return topHomeActivity == null || !topHomeActivity.isHomeActivity();
+            return topHomeActivity == null || !topHomeActivity.isHomeActivity() ?
+                    STACK_VISIBLE : STACK_INVISIBLE;
         }
 
         // Find the first stack below focused stack that actually got something visible.
@@ -1399,7 +1429,7 @@
         if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
                 && stackIndex == belowFocusedIndex) {
             // Stacks directly behind the docked or pinned stack are always visible.
-            return true;
+            return STACK_VISIBLE;
         }
 
         if (focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
@@ -1408,7 +1438,7 @@
             // visible so they can act as a backdrop to the translucent activity.
             // For example, dialog activities
             if (stackIndex == belowFocusedIndex) {
-                return true;
+                return STACK_VISIBLE;
             }
             if (belowFocusedIndex >= 0) {
                 final ActivityStack stack = mStacks.get(belowFocusedIndex);
@@ -1416,14 +1446,14 @@
                         && stackIndex == (belowFocusedIndex - 1)) {
                     // The stack behind the docked or pinned stack is also visible so we can have a
                     // complete backdrop to the translucent activity when the docked stack is up.
-                    return true;
+                    return STACK_VISIBLE;
                 }
             }
         }
 
         if (StackId.isStaticStack(mStackId)) {
             // Visibility of any static stack should have been determined by the conditions above.
-            return false;
+            return STACK_INVISIBLE;
         }
 
         for (int i = stackIndex + 1; i < mStacks.size(); i++) {
@@ -1435,15 +1465,15 @@
 
             if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
                 // These stacks can't have any dynamic stacks visible behind them.
-                return false;
+                return STACK_INVISIBLE;
             }
 
             if (!hasTranslucentActivity(stack)) {
-                return false;
+                return STACK_INVISIBLE;
             }
         }
 
-        return true;
+        return STACK_VISIBLE;
     }
 
     final int rankTaskLayers(int baseLayer) {
@@ -1476,14 +1506,16 @@
         // If the top activity is not fullscreen, then we need to
         // make sure any activities under it are now visible.
         boolean aboveTop = top != null;
-        final boolean stackInvisible = !isStackVisibleLocked();
+        final int stackVisibility = getStackVisibilityLocked();
+        final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
+        final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND;
         boolean behindFullscreenActivity = stackInvisible;
-        boolean noStackActivityResumed = (isInStackLocked(starting) == null);
-
+        boolean resumeNextActivity = isFocusable() && (isInStackLocked(starting) == null);
+        boolean behindTranslucentActivity = false;
+        final ActivityRecord visibleBehind = getVisibleBehindActivity();
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             final ArrayList<ActivityRecord> activities = task.mActivities;
-
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.finishing) {
@@ -1496,11 +1528,13 @@
                 aboveTop = false;
                 // mLaunchingBehind: Activities launching behind are at the back of the task stack
                 // but must be drawn initially for the animation as though they were visible.
-                if ((!behindFullscreenActivity || r.mLaunchTaskBehind)
-                        && okToShowLocked(r)) {
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                            "Make visible? " + r + " finishing=" + r.finishing
-                            + " state=" + r.state);
+                final boolean activityVisibleBehind =
+                        (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == r;
+                final boolean isVisible = (!behindFullscreenActivity || r.mLaunchTaskBehind
+                        || activityVisibleBehind) && okToShowLocked(r);
+                if (isVisible) {
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
+                            + " finishing=" + r.finishing + " state=" + r.state);
                     // First: if this is not the current activity being started, make
                     // sure it matches the current configuration.
                     if (r != starting) {
@@ -1509,18 +1543,18 @@
 
                     if (r.app == null || r.app.thread == null) {
                         if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
-                                noStackActivityResumed, r)) {
+                                resumeNextActivity, r)) {
                             if (activityNdx >= activities.size()) {
                                 // Record may be removed if its process needs to restart.
                                 activityNdx = activities.size() - 1;
                             } else {
-                                noStackActivityResumed = false;
+                                resumeNextActivity = false;
                             }
                         }
                     } else if (r.visible) {
                         // If this activity is already visible, then there is nothing to do here.
                         if (handleAlreadyVisible(r)) {
-                            noStackActivityResumed = false;
+                            resumeNextActivity = false;
                         }
                     } else {
                         makeVisible(starting, r);
@@ -1529,15 +1563,23 @@
                     configChanges |= r.configChangeFlags;
                     behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
                             behindFullscreenActivity, task, r);
+                    if (behindFullscreenActivity && !r.fullscreen) {
+                        behindTranslucentActivity = true;
+                    }
                 } else {
-                    makeInvisible(stackInvisible, behindFullscreenActivity, r);
+                    if (DEBUG_VISIBILITY || true) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+                            + " finishing=" + r.finishing + " state=" + r.state + " stackInvisible="
+                            + stackInvisible + " behindFullscreenActivity="
+                            + behindFullscreenActivity + " mLaunchTaskBehind="
+                            + r.mLaunchTaskBehind);
+                    makeInvisible(r, visibleBehind);
                 }
             }
             if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
                 // The visibility of tasks and the activities they contain in freeform stack are
                 // determined individually unlike other stacks where the visibility or fullscreen
                 // status of an activity in a previous task affects other.
-                behindFullscreenActivity = stackInvisible;
+                behindFullscreenActivity = stackVisibility == STACK_INVISIBLE;
             }
         }
 
@@ -1561,7 +1603,7 @@
     }
 
     private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
-            boolean isTop, boolean noStackActivityResumed, ActivityRecord r) {
+            boolean isTop, boolean andResume, ActivityRecord r) {
         // We need to make sure the app is running if it's the top, or it is just made visible from
         // invisible. If the app is already visible, it must have died while it was visible. In this
         // case, we'll show the dead window but will not restart the app. Otherwise we could end up
@@ -1578,25 +1620,21 @@
                 setVisible(r, true);
             }
             if (r != starting) {
-                mStackSupervisor.startSpecificActivityLocked(r, noStackActivityResumed, false);
+                mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
                 return true;
             }
         }
         return false;
     }
 
-    private void makeInvisible(boolean stackInvisible, boolean behindFullscreenActivity,
-            ActivityRecord r) {
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r + " finishing="
-                + r.finishing + " state=" + r.state + " stackInvisible=" + stackInvisible
-                + " behindFullscreenActivity=" + behindFullscreenActivity);
+    private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
         if (!r.visible) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
             return;
         }
         // Now for any activities that aren't visible to the user, make sure they no longer are
         // keeping the screen frozen.
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r);
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state);
         try {
             setVisible(r, false);
             switch (r.state) {
@@ -1615,7 +1653,7 @@
                 case PAUSED:
                     // This case created for transitioning activities from
                     // translucent to opaque {@link Activity#convertToOpaque}.
-                    if (getVisibleBehindActivity() == r) {
+                    if (visibleBehind == r) {
                         releaseBackgroundResources(r);
                     } else {
                         if (!mStackSupervisor.mStoppingActivities.contains(r)) {
@@ -1637,16 +1675,16 @@
     private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
             TaskRecord task, ActivityRecord r) {
         if (r.fullscreen) {
-            // At this point, nothing else needs to be shown in this task.
-            behindFullscreenActivity = true;
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
                     + " stackInvisible=" + stackInvisible
                     + " behindFullscreenActivity=" + behindFullscreenActivity);
-        } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
+            // At this point, nothing else needs to be shown in this task.
             behindFullscreenActivity = true;
+        } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
                     + " stackInvisible=" + stackInvisible
                     + " behindFullscreenActivity=" + behindFullscreenActivity);
+            behindFullscreenActivity = true;
         }
         return behindFullscreenActivity;
     }
@@ -1772,15 +1810,17 @@
      *
      * @param prev The previously resumed activity, for when in the process
      * of pausing; can be null to call from elsewhere.
+     * @param options Activity options.
      *
      * @return Returns true if something is being resumed, or false if
      * nothing happened.
+     *
+     * NOTE: It is not safe to call this method directly as it can cause an activity in a
+     *       non-focused stack to be resumed.
+     *       Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the
+     *       right activity for the current system state.
      */
-    final boolean resumeTopActivityLocked(ActivityRecord prev) {
-        return resumeTopActivityLocked(prev, null);
-    }
-
-    final boolean resumeTopActivityLocked(ActivityRecord prev, ActivityOptions options) {
+    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
         if (mStackSupervisor.inResumeTopActivity) {
             // Don't even start recursing.
             return false;
@@ -1831,14 +1871,13 @@
         if (next == null) {
             // There are no more activities!
             final String reason = "noMoreActivities";
-            if (!mFullscreen) {
+            if (!mFullscreen && adjustFocusToNextFocusableStackLocked(reason)) {
                 // Try to move focus to the next visible stack with a running activity if this
                 // stack is not covering the entire screen.
-                final ActivityStack stack = getNextVisibleStackLocked();
-                if (adjustFocusToNextVisibleStackLocked(stack, reason)) {
-                    return mStackSupervisor.resumeTopActivitiesLocked(stack, prev, null);
-                }
+                return mStackSupervisor.resumeFocusedStackTopActivityLocked(
+                        mStackSupervisor.getFocusedStack(), prev, null);
             }
+
             // Let's just start up the Launcher...
             ActivityOptions.abort(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
@@ -2303,11 +2342,12 @@
             ActivityStack lastStack = mStackSupervisor.getLastStack();
             final boolean fromHome = lastStack.isHomeStack();
             if (!isHomeStack() && (fromHome || topTask() != task)) {
-                task.setTaskToReturnTo(fromHome
-                        ? lastStack.topTask() == null
-                                ? HOME_ACTIVITY_TYPE
-                                : lastStack.topTask().taskType
-                        : APPLICATION_ACTIVITY_TYPE);
+                int returnToType = APPLICATION_ACTIVITY_TYPE;
+                if (fromHome && StackId.allowTopTaskToReturnHome(mStackId)) {
+                    returnToType = lastStack.topTask() == null
+                            ? HOME_ACTIVITY_TYPE : lastStack.topTask().taskType;
+                }
+                task.setTaskToReturnTo(returnToType);
             }
         } else {
             task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
@@ -2577,8 +2617,9 @@
                     if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to bottom task " + bottom.task);
                 } else {
-                    targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
-                            null, null, null, false);
+                    targetTask = createTaskRecord(
+                            mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
+                            target.info, null, null, null, false);
                     targetTask.affinityIntent = target.intent;
                     if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to new task " + target.task);
@@ -2870,7 +2911,7 @@
         final ActivityRecord next = topRunningActivityLocked();
         final String myReason = reason + " adjustFocus";
         if (next != r) {
-            if (next != null && StackId.keepFocusInStackIfPossible(mStackId)) {
+            if (next != null && StackId.keepFocusInStackIfPossible(mStackId) && isFocusable()) {
                 // For freeform, docked, and pinned stacks we always keep the focus within the
                 // stack as long as there is a running activity in the stack that we can adjust
                 // focus to.
@@ -2882,8 +2923,7 @@
                     // For non-fullscreen stack, we want to move the focus to the next visible
                     // stack to prevent the home screen from moving to the top and obscuring
                     // other visible stacks.
-                    if (!mFullscreen
-                            && adjustFocusToNextVisibleStackLocked(null, myReason)) {
+                    if (!mFullscreen && adjustFocusToNextFocusableStackLocked(myReason)) {
                         return;
                     }
                     // Move the home stack to the top if this stack is fullscreen or there is no
@@ -2897,24 +2937,16 @@
             }
         }
 
-        final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
-        if (top != null) {
-            mService.setFocusedActivityLocked(top, myReason);
-        }
+        mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked(), myReason);
     }
 
-    private boolean adjustFocusToNextVisibleStackLocked(ActivityStack inStack, String reason) {
-        final ActivityStack stack = (inStack != null) ? inStack : getNextVisibleStackLocked();
-        final String myReason = reason + " adjustFocusToNextVisibleStack";
+    private boolean adjustFocusToNextFocusableStackLocked(String reason) {
+        final ActivityStack stack = getNextFocusableStackLocked();
+        final String myReason = reason + " adjustFocusToNextFocusableStack";
         if (stack == null) {
             return false;
         }
-        final ActivityRecord top = stack.topRunningActivityLocked();
-        if (top == null) {
-            return false;
-        }
-        mService.setFocusedActivityLocked(top, myReason);
-        return true;
+        return mService.setFocusedActivityLocked(stack.topRunningActivityLocked(), myReason);
     }
 
     final void stopActivityLocked(ActivityRecord r) {
@@ -2966,7 +2998,7 @@
                 r.stopped = true;
                 if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
                 r.state = ActivityState.STOPPED;
-                if (r.configDestroy) {
+                if (r.deferRelaunchUntilPaused) {
                     destroyActivityLocked(r, true, "stop-except");
                 }
             }
@@ -3224,7 +3256,7 @@
             r.makeFinishingLocked();
             boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
             if (activityRemoved) {
-                mStackSupervisor.resumeTopActivitiesLocked();
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
             }
             if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
                     "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -3237,7 +3269,7 @@
         if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
         mStackSupervisor.mFinishingActivities.add(r);
         r.resumeKeyDispatchingLocked();
-        mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+        mStackSupervisor.resumeFocusedStackTopActivityLocked();
         return r;
     }
 
@@ -3362,9 +3394,9 @@
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
-                    int res = mService.mActivityStarter.startActivityLocked(srec.app.thread, destIntent,
-                            null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null, null,
-                            parent.appToken, null, 0, -1, parent.launchedFromUid,
+                    int res = mService.mActivityStarter.startActivityLocked(srec.app.thread,
+                            destIntent, null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null,
+                            null, parent.appToken, null, 0, -1, parent.launchedFromUid,
                             parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
                             false, true, null, null, null);
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
@@ -3395,9 +3427,9 @@
         if (mPausingActivity == r) {
             mPausingActivity = null;
         }
-        mService.clearFocusedActivity(r);
+        mService.resetFocusedActivityIfNeededLocked(r);
 
-        r.configDestroy = false;
+        r.deferRelaunchUntilPaused = false;
         r.frozenBeforeDestroy = false;
 
         if (setState) {
@@ -3526,7 +3558,7 @@
             }
         }
         if (activityRemoved) {
-            mStackSupervisor.resumeTopActivitiesLocked();
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
         }
     }
 
@@ -3700,7 +3732,7 @@
                     removeActivityFromHistoryLocked(r, reason);
                 }
             }
-            mStackSupervisor.resumeTopActivitiesLocked();
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
@@ -3709,9 +3741,9 @@
     void releaseBackgroundResources(ActivityRecord r) {
         if (hasVisibleBehindActivity() &&
                 !mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
-            if (r == topRunningActivityLocked()) {
+            if (r == topRunningActivityLocked() && getStackVisibilityLocked() == STACK_VISIBLE) {
                 // Don't release the top activity if it has requested to run behind the next
-                // activity.
+                // activity and the stack is currently visible.
                 return;
             }
             if (DEBUG_STATES) Slog.d(TAG_STATES, "releaseBackgroundResources activtyDisplay=" +
@@ -3738,7 +3770,7 @@
             setVisibleBehindActivity(null);
             mStackSupervisor.scheduleIdleTimeoutLocked(null);
         }
-        mStackSupervisor.resumeTopActivitiesLocked();
+        mStackSupervisor.resumeFocusedStackTopActivityLocked();
     }
 
     boolean hasVisibleBehindActivity() {
@@ -3953,7 +3985,7 @@
             updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
         }
 
-        mStackSupervisor.resumeTopActivitiesLocked();
+        mStackSupervisor.resumeFocusedStackTopActivityLocked();
         EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
 
         if (VALIDATE_TOKENS) {
@@ -4016,7 +4048,7 @@
             if (fullscreenStack != null && fullscreenStack.hasVisibleBehindActivity()) {
                 final ActivityRecord visibleBehind = fullscreenStack.getVisibleBehindActivity();
                 mService.setFocusedActivityLocked(visibleBehind, "moveTaskToBack");
-                mStackSupervisor.resumeTopActivitiesLocked(fullscreenStack, null, null);
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 return true;
             }
         }
@@ -4071,7 +4103,7 @@
             return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack");
         }
 
-        mStackSupervisor.resumeTopActivitiesLocked();
+        mStackSupervisor.resumeFocusedStackTopActivityLocked();
         return true;
     }
 
@@ -4169,38 +4201,33 @@
             r.configChangeFlags |= changes;
             r.startFreezingScreenLocked(r.app, globalChanges);
             r.forceNewConfig = false;
+            preserveWindow &= isResizeOnlyChange(changes);
             if (r.app == null || r.app.thread == null) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is destroying non-running " + r);
                 destroyActivityLocked(r, true, "config");
             } else if (r.state == ActivityState.PAUSING) {
-                // A little annoying: we are waiting for this activity to
-                // finish pausing.  Let's not do anything now, but just
-                // flag that it needs to be restarted when done pausing.
+                // A little annoying: we are waiting for this activity to finish pausing. Let's not
+                // do anything now, but just flag that it needs to be restarted when done pausing.
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is skipping already pausing " + r);
-                r.configDestroy = true;
+                r.deferRelaunchUntilPaused = true;
+                r.preserveWindowOnDeferredRelaunch = preserveWindow;
                 return true;
             } else if (r.state == ActivityState.RESUMED) {
-                // Try to optimize this case: the configuration is changing
-                // and we need to restart the top, resumed activity.
-                // Instead of doing the normal handshaking, just say
+                // Try to optimize this case: the configuration is changing and we need to restart
+                // the top, resumed activity. Instead of doing the normal handshaking, just say
                 // "restart!".
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is relaunching resumed " + r);
-                relaunchActivityLocked(r, r.configChangeFlags, true,
-                        preserveWindow && isResizeOnlyChange(changes));
-                r.configChangeFlags = 0;
+                relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow);
             } else {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is relaunching non-resumed " + r);
-                relaunchActivityLocked(r, r.configChangeFlags, false,
-                        preserveWindow && isResizeOnlyChange(changes));
-                r.configChangeFlags = 0;
+                relaunchActivityLocked(r, r.configChangeFlags, false, preserveWindow);
             }
 
-            // All done...  tell the caller we weren't able to keep this
-            // activity around.
+            // All done...  tell the caller we weren't able to keep this activity around.
             return false;
         }
 
@@ -4223,20 +4250,20 @@
         int taskChanges = oldTaskOverride.diff(taskConfig);
         // We don't want to use size changes if they don't cross boundaries that are important to
         // the app.
-        if ((taskChanges & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+        if ((taskChanges & CONFIG_SCREEN_SIZE) != 0) {
             final boolean crosses = record.crossesHorizontalSizeThreshold(
                     oldTaskOverride.screenWidthDp, taskConfig.screenWidthDp)
                     || record.crossesVerticalSizeThreshold(
                     oldTaskOverride.screenHeightDp, taskConfig.screenHeightDp);
             if (!crosses) {
-                taskChanges &= ~ActivityInfo.CONFIG_SCREEN_SIZE;
+                taskChanges &= ~CONFIG_SCREEN_SIZE;
             }
         }
-        if ((taskChanges & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+        if ((taskChanges & CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
             final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
             final int newSmallest = taskConfig.smallestScreenWidthDp;
             if (!record.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) {
-                taskChanges &= ~ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+                taskChanges &= ~CONFIG_SMALLEST_SCREEN_SIZE;
             }
         }
         return catchConfigChangesFromUnset(taskConfig, oldTaskOverride, taskChanges);
@@ -4248,7 +4275,7 @@
             // {@link Configuration#diff} doesn't catch changes from unset values.
             // Check for changes we care about.
             if (oldTaskOverride.orientation != taskConfig.orientation) {
-                taskChanges |= ActivityInfo.CONFIG_ORIENTATION;
+                taskChanges |= CONFIG_ORIENTATION;
             }
             // We want to explicitly track situations where the size configuration goes from
             // undefined to defined. We don't care about crossing the threshold in that case,
@@ -4258,34 +4285,41 @@
             final int undefinedHeight = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
             if ((oldHeight == undefinedHeight && newHeight != undefinedHeight)
                     || (oldHeight != undefinedHeight && newHeight == undefinedHeight)) {
-                taskChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
+                taskChanges |= CONFIG_SCREEN_SIZE;
             }
             final int oldWidth = oldTaskOverride.screenWidthDp;
             final int newWidth = taskConfig.screenWidthDp;
             final int undefinedWidth = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
             if ((oldWidth == undefinedWidth && newWidth != undefinedWidth)
                     || (oldWidth != undefinedWidth && newWidth == undefinedWidth)) {
-                taskChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
+                taskChanges |= CONFIG_SCREEN_SIZE;
             }
             final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
             final int newSmallest = taskConfig.smallestScreenWidthDp;
             final int undefinedSmallest = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
             if ((oldSmallest == undefinedSmallest && newSmallest != undefinedSmallest)
                     || (oldSmallest != undefinedSmallest && newSmallest == undefinedSmallest)) {
-                taskChanges |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+                taskChanges |= CONFIG_SMALLEST_SCREEN_SIZE;
+            }
+            final int oldLayout = oldTaskOverride.screenLayout;
+            final int newLayout = taskConfig.screenLayout;
+            if ((oldLayout == SCREENLAYOUT_UNDEFINED && newLayout != SCREENLAYOUT_UNDEFINED)
+                || (oldLayout != SCREENLAYOUT_UNDEFINED && newLayout == SCREENLAYOUT_UNDEFINED)) {
+                taskChanges |= CONFIG_SCREEN_LAYOUT;
             }
         }
         return taskChanges;
     }
 
     private static boolean isResizeOnlyChange(int change) {
-        return (change & ~(ActivityInfo.CONFIG_SCREEN_SIZE
-                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) == 0;
+        return (change & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
+                | CONFIG_SCREEN_LAYOUT)) == 0;
     }
 
     private void relaunchActivityLocked(
             ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) {
         if (mService.mSuppressResizeConfigChanges && preserveWindow) {
+            r.configChangeFlags = 0;
             return;
         }
 
@@ -4313,6 +4347,7 @@
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
                     !andResume, new Configuration(mService.mConfiguration),
                     new Configuration(r.task.mOverrideConfig), preserveWindow);
+            mStackSupervisor.activityRelaunchingLocked(r);
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
@@ -4328,6 +4363,8 @@
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
             r.state = ActivityState.PAUSED;
         }
+
+        r.configChangeFlags = 0;
     }
 
     boolean willActivityBeVisibleLocked(IBinder token) {
@@ -4593,7 +4630,7 @@
                     a.forceNewConfig = true;
                     if (starting != null && a == starting && a.visible) {
                         a.startFreezingScreenLocked(starting.app,
-                                ActivityInfo.CONFIG_SCREEN_LAYOUT);
+                                CONFIG_SCREEN_LAYOUT);
                     }
                 }
             }
@@ -4661,7 +4698,7 @@
             // We only need to adjust focused stack if this stack is in focus.
             if (isOnHomeDisplay() && mStackSupervisor.isFocusedStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
-                if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
+                if (mFullscreen || !adjustFocusToNextFocusableStackLocked(myReason)) {
                     mStackSupervisor.moveHomeStackToFront(myReason);
                 }
             }
@@ -4734,10 +4771,7 @@
 
     private void postAddTask(TaskRecord task, ActivityStack prevStack) {
         if (prevStack != null) {
-            if (prevStack != this
-                    && (prevStack.mStackId == PINNED_STACK_ID || mStackId == PINNED_STACK_ID)) {
-                task.reportPictureInPictureModeChange();
-            }
+            task.reportPictureInPictureModeChangeIfNeeded(prevStack);
         } else if (task.voiceSession != null) {
             try {
                 task.voiceSession.taskStarted(task.intent, task.taskId);
@@ -4747,36 +4781,31 @@
     }
 
     void addConfigOverride(ActivityRecord r, TaskRecord task) {
-        final Rect bounds = task.getLaunchBounds();
-        task.updateOverrideConfiguration(bounds);
+        final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
-                r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind,
-                bounds, task.mOverrideConfig, !r.isHomeActivity());
+                (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
+                task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
+                !r.isHomeActivity(), r.isAlwaysFocusable());
         mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
         r.taskConfigOverride = task.mOverrideConfig;
     }
 
-    void setFocusAndResumeStateIfNeeded(
-            ActivityRecord r, boolean setFocus, boolean setResume, String reason) {
-        // If the activity had focus before move focus to this stack.
-        if (setFocus) {
-            // If the activity owns the last resumed activity, transfer that together,
-            // so that we don't resume the same activity again in the new stack.
-            // Apps may depend on onResume()/onPause() being called in pairs.
-            if (setResume) {
-                mResumedActivity = r;
-                // Move the stack in which we are placing the activity to the front. We don't use
-                // ActivityManagerService.setFocusedActivityLocked, because if the activity is
-                // already focused, the call will short-circuit and do nothing.
-                moveToFront(reason);
-            } else {
-                // We need to not only move the stack to the front, but also have the activity
-                // focused. This will achieve both goals.
-                mService.setFocusedActivityLocked(r, reason);
-            }
+    void moveToFrontAndResumeStateIfNeeded(
+            ActivityRecord r, boolean moveToFront, boolean setResume, String reason) {
+        if (!moveToFront) {
+            return;
         }
+
+        // If the activity owns the last resumed activity, transfer that together,
+        // so that we don't resume the same activity again in the new stack.
+        // Apps may depend on onResume()/onPause() being called in pairs.
+        if (setResume) {
+            mResumedActivity = r;
+        }
+        // Move the stack in which we are placing the activity to the front. The call will also
+        // make sure the activity focus is set.
+        moveToFront(reason);
     }
 
     /**
@@ -4796,19 +4825,22 @@
         final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
 
         final TaskRecord task = createTaskRecord(
-                mStackSupervisor.getNextTaskId(), r.info, r.intent, null, null, true);
+                mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
+                r.info, r.intent, null, null, true);
         r.setTask(task, null);
         task.addActivityToTop(r);
         setAppTask(r, task);
-        task.reportPictureInPictureModeChange();
-        setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+        task.reportPictureInPictureModeChangeIfNeeded(prevStack);
+        moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+        if (wasResumed) {
+            prevStack.mResumedActivity = null;
+        }
     }
 
     private void setAppTask(ActivityRecord r, TaskRecord task) {
-        final Rect bounds = task.getLaunchBounds();
-        task.updateOverrideConfiguration(bounds);
+        final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
         mWindowManager.setAppTask(
-                r.appToken, task.taskId, mStackId, task.getLaunchBounds(), task.mOverrideConfig);
+                r.appToken, task.taskId, mStackId, bounds, task.mOverrideConfig);
         mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
         r.taskConfigOverride = task.mOverrideConfig;
     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 32671acf..09bb9ab 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -16,70 +16,6 @@
 
 package com.android.server.am;
 
-import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
-import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.ActivityManager.RESIZE_MODE_FORCED;
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
-
 import android.Manifest;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -164,6 +100,70 @@
 import java.util.List;
 import java.util.Set;
 
+import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+
 public final class ActivityStackSupervisor implements DisplayListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
     private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
@@ -262,10 +262,12 @@
     // For debugging to make sure the caller when acquiring/releasing our
     // wake lock is the system process.
     static final boolean VALIDATE_WAKE_LOCK_CALLER = false;
+    /** The number of distinct task ids that can be assigned to the tasks of a single user */
+    private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
 
     final ActivityManagerService mService;
 
-    private final RecentTasks mRecentTasks;
+    private RecentTasks mRecentTasks;
 
     final ActivityStackSupervisorHandler mHandler;
 
@@ -276,9 +278,11 @@
     /** Counter for next free stack ID to use for dynamic activity stacks. */
     private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID;
 
-    /** Task identifier that activities are currently being started in.  Incremented each time a
-     * new task is created. */
-    private int mCurTaskId = 0;
+    /**
+     * Maps the task identifier that activities are currently being started in to the userId of the
+     * task. Each time a new task is created, the entry for the userId of the task is incremented
+     */
+    private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
 
     /** The current user */
     int mCurrentUser;
@@ -370,7 +374,7 @@
      */
     private LockTaskNotify mLockTaskNotify;
 
-    /** Used to keep resumeTopActivityLocked() from being entered recursively */
+    /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
     boolean inResumeTopActivity;
 
     // temp. rects used during resize calculation so we don't need to create a new object each time.
@@ -379,6 +383,7 @@
 
     private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>();
     private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
+    private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
 
     // The default minimal size that will be used if the activity doesn't specify its minimal size.
     // It will be calculated when the default display gets added.
@@ -389,6 +394,8 @@
 
     private final ActivityMetricsLogger mActivityMetricsLogger;
 
+    private final ResizeDockedStackTimeout mResizeDockedStackTimeout;
+
     static class FindTaskResult {
         ActivityRecord r;
         boolean matchedByRootAffinity;
@@ -396,6 +403,12 @@
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
     /**
+     * Used to keep track whether app visibilities got changed since the last pause. Useful to
+     * determine whether to invoke the task stack change listener after pausing.
+     */
+    boolean mAppVisibilitiesChangedSinceLastPause;
+
+    /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
      */
@@ -427,11 +440,15 @@
         }
     }
 
-    public ActivityStackSupervisor(ActivityManagerService service, RecentTasks recentTasks) {
+    public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
-        mRecentTasks = recentTasks;
         mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
         mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
+        mResizeDockedStackTimeout = new ResizeDockedStackTimeout(service, this, mHandler);
+    }
+
+    void setRecentTasks(RecentTasks recentTasks) {
+        mRecentTasks = recentTasks;
     }
 
     /**
@@ -492,8 +509,8 @@
                 calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
             }
 
-            createStackOnDisplay(HOME_STACK_ID, Display.DEFAULT_DISPLAY, true);
-            mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
+            mHomeStack = mFocusedStack = mLastFocusedStack =
+                    getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
 
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         }
@@ -537,16 +554,30 @@
         return stack == mHomeStack.mStacks.get((mHomeStack.mStacks.size() - 1));
     }
 
-    void setFocusStack(String reason, ActivityStack focusedStack) {
-        mLastFocusedStack = mFocusedStack;
-        mFocusedStack = focusedStack;
+    /** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
+    void setFocusStackUnchecked(String reason, ActivityStack focusCandidate) {
+        if (!focusCandidate.isFocusable()) {
+            // The focus candidate isn't focusable. Move focus to the top stack that is focusable.
+            focusCandidate = focusCandidate.getNextFocusableStackLocked();
+        }
 
-        EventLogTags.writeAmFocusedStack(
-                mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
-                mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
+        if (focusCandidate != mFocusedStack) {
+            mLastFocusedStack = mFocusedStack;
+            mFocusedStack = focusCandidate;
+
+            EventLogTags.writeAmFocusedStack(
+                    mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+                    mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
+        }
+
+        final ActivityRecord r = topRunningActivityLocked();
+        if (mService.mFocusedActivity != r) {
+            // The focus activity should always be the top activity in the focused stack.
+            // There will be chaos and anarchy if it isn't...
+            mService.setFocusedActivityLocked(r, reason + " setFocusStack");
+        }
 
         if (mService.mBooting || !mService.mBooted) {
-            final ActivityRecord r = topRunningActivityLocked();
             if (r != null && r.idle) {
                 checkFinishBootingLocked();
             }
@@ -591,12 +622,14 @@
 
         mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
         ActivityRecord r = getHomeActivity();
+        final String myReason = reason + " resumeHomeStackTask";
+
         // Only resume home activity if isn't finishing.
         if (r != null && !r.finishing) {
-            mService.setFocusedActivityLocked(r, reason);
-            return resumeTopActivitiesLocked(mHomeStack, prev, null);
+            mService.setFocusedActivityLocked(r, myReason);
+            return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
         }
-        return mService.startHomeActivityLocked(mCurrentUser, reason);
+        return mService.startHomeActivityLocked(mCurrentUser, myReason);
     }
 
     TaskRecord anyTaskForIdLocked(int id) {
@@ -659,20 +692,37 @@
         return null;
     }
 
-    void setNextTaskId(int taskId) {
-        if (taskId > mCurTaskId) {
-            mCurTaskId = taskId;
+    void setNextTaskIdForUserLocked(int taskId, int userId) {
+        final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
+        if (taskId > currentTaskId) {
+            mCurTaskIdForUser.put(userId, taskId);
         }
     }
 
-    int getNextTaskId() {
-        do {
-            mCurTaskId++;
-            if (mCurTaskId <= 0) {
-                mCurTaskId = 1;
+    int getNextTaskIdForUserLocked(int userId) {
+        mRecentTasks.loadUserRecentsLocked(userId);
+        final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
+        // for a userId u, a taskId can only be in the range
+        // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
+        // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
+        int candidateTaskId = currentTaskId;
+        while (anyTaskForIdLocked(candidateTaskId, !RESTORE_FROM_RECENTS,
+                INVALID_STACK_ID) != null) {
+            candidateTaskId++;
+            if (candidateTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
+                // Wrap around as there will be smaller task ids that are available now.
+                candidateTaskId -= MAX_TASK_IDS_PER_USER;
             }
-        } while (anyTaskForIdLocked(mCurTaskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null);
-        return mCurTaskId;
+            if (candidateTaskId == currentTaskId) {
+                // Something wrong!
+                // All MAX_TASK_IDS_PER_USER task ids are taken up by running tasks for this user
+                throw new IllegalStateException("Cannot get an available task id."
+                        + " Reached limit of " + MAX_TASK_IDS_PER_USER
+                        + " running tasks per user.");
+            }
+        }
+        mCurTaskIdForUser.put(userId, candidateTaskId);
+        return candidateTaskId;
     }
 
     ActivityRecord resumedAppLocked() {
@@ -893,7 +943,7 @@
         final ArrayList<ActivityStack> stacks = mHomeStack.mStacks;
         for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = stacks.get(stackNdx);
-            if (stack != focusedStack && isFrontStack(stack)) {
+            if (stack != focusedStack && isFrontStack(stack) && stack.isFocusable()) {
                 r = stack.topRunningActivityLocked();
                 if (r != null) {
                     return r;
@@ -1148,14 +1198,12 @@
             // a resume.
             stack.minimalResumeActivityLocked(r);
         } else {
-            // This activity is not starting in the resumed state... which
-            // should look like we asked it to pause+stop (but remain visible),
-            // and it has done so and reported back the current icicle and
-            // other state.
+            // This activity is not starting in the resumed state... which should look like we asked
+            // it to pause+stop (but remain visible), and it has done so and reported back the
+            // current icicle and other state.
             if (DEBUG_STATES) Slog.v(TAG_STATES,
-                    "Moving to STOPPED: " + r + " (starting in stopped state)");
-            r.state = STOPPED;
-            r.stopped = true;
+                    "Moving to PAUSED: " + r + " (starting in paused state)");
+            r.state = PAUSED;
         }
 
         // Launch the new version setup screen if needed.  We do this -after-
@@ -1345,30 +1393,20 @@
         return ACTIVITY_RESTRICTION_NONE;
     }
 
-    boolean setFocusedStack(ActivityRecord r, String reason) {
+    boolean moveActivityStackToFront(ActivityRecord r, String reason) {
         if (r == null) {
             // Not sure what you are trying to do, but it is not going to work...
             return false;
         }
         final TaskRecord task = r.task;
         if (task == null || task.stack == null) {
-            Slog.w(TAG, "Can't set focus stack for r=" + r + " task=" + task);
+            Slog.w(TAG, "Can't move stack to front for r=" + r + " task=" + task);
             return false;
         }
         task.stack.moveToFront(reason, task);
         return true;
     }
 
-    Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
-        Rect newBounds = null;
-        if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
-            if (canUseActivityOptionsLaunchBounds(options)) {
-                newBounds = options.getLaunchBounds();
-            }
-        }
-        return newBounds;
-    }
-
     void setLaunchSource(int uid) {
         mLaunchingActivity.setWorkSource(new WorkSource(uid));
     }
@@ -1518,7 +1556,7 @@
         //mWindowManager.dump();
 
         if (activityRemoved) {
-            resumeTopActivitiesLocked();
+            resumeFocusedStackTopActivityLocked();
         }
 
         return r;
@@ -1611,35 +1649,17 @@
         }
     }
 
-    boolean resumeTopActivitiesLocked() {
-        return resumeTopActivitiesLocked(null, null, null);
+    boolean resumeFocusedStackTopActivityLocked() {
+        return resumeFocusedStackTopActivityLocked(null, null, null);
     }
 
-    boolean resumeTopActivitiesLocked(
+    boolean resumeFocusedStackTopActivityLocked(
             ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
-        if (targetStack == null) {
-            targetStack = mFocusedStack;
+        if (targetStack != null && isFocusedStack(targetStack)) {
+            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
         }
-        // Do targetStack first.
-        boolean result = false;
-        if (isFocusedStack(targetStack)) {
-            result = targetStack.resumeTopActivityLocked(target, targetOptions);
-        }
-
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
-            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = stacks.get(stackNdx);
-                if (stack == targetStack) {
-                    // Already started above.
-                    continue;
-                }
-                if (isFocusedStack(stack)) {
-                    stack.resumeTopActivityLocked(null);
-                }
-            }
-        }
-        return result;
+        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+        return false;
     }
 
     void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
@@ -1681,10 +1701,13 @@
         }
 
         if (task.mResizeable && options != null) {
-            if (canUseActivityOptionsLaunchBounds(options)) {
-                Rect bounds = options.getLaunchBounds();
+            int stackId = options.getLaunchStackId();
+            if (canUseActivityOptionsLaunchBounds(options, stackId)) {
+                final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
                 task.updateOverrideConfiguration(bounds);
-                final int stackId = task.getLaunchStackId();
+                if (stackId == INVALID_STACK_ID) {
+                    stackId = task.getLaunchStackId();
+                }
                 if (stackId != task.stack.mStackId) {
                     moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
                     // moveTaskToStackUncheckedLocked() should already placed the task on top,
@@ -1706,10 +1729,14 @@
                 "findTaskToMoveToFront: moved to front of stack=" + task.stack);
     }
 
-    private boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
+    boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
         // We use the launch bounds in the activity options is the device supports freeform
-        // window management.
-        return options.hasLaunchBounds() && mService.mSupportsFreeformWindowManagement;
+        // window management or is launching into the pinned stack.
+        if (!options.hasLaunchBounds()) {
+            return false;
+        }
+        return (mService.mSupportsPictureInPicture && launchStackId == PINNED_STACK_ID)
+                || mService.mSupportsFreeformWindowManagement;
     }
 
     ActivityStack getStack(int stackId) {
@@ -1806,16 +1833,20 @@
         }
     }
 
-    void resizeStackLocked(int stackId, Rect bounds, boolean preserveWindows,
-            boolean allowResizeInDockedMode) {
+    void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
+            boolean preserveWindows, boolean allowResizeInDockedMode) {
+        if (stackId == DOCKED_STACK_ID) {
+            resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
+                    preserveWindows);
+            return;
+        }
         final ActivityStack stack = getStack(stackId);
         if (stack == null) {
             Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
             return;
         }
 
-        if (!allowResizeInDockedMode
-                && stackId != DOCKED_STACK_ID && getStack(DOCKED_STACK_ID) != null) {
+        if (!allowResizeInDockedMode && getStack(DOCKED_STACK_ID) != null) {
             // If the docked stack exist we don't allow resizes of stacks not caused by the docked
             // stack size changing so things don't get out of sync.
             return;
@@ -1824,100 +1855,145 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
         mWindowManager.deferSurfaceLayout();
         try {
-
-            if (bounds != null && mWindowManager.isFullscreenBounds(stackId, bounds)) {
-                // The bounds passed in corresponds to the fullscreen bounds which we normally
-                // represent with null. Go ahead and set it to null so that all tasks configuration
-                // can have the right fullscreen state.
-                bounds = null;
-            }
-
-            ActivityRecord r = stack.topRunningActivityLocked();
-
-            mTmpBounds.clear();
-            mTmpConfigs.clear();
-            ArrayList<TaskRecord> tasks = stack.getAllTasks();
-            for (int i = tasks.size() - 1; i >= 0; i--) {
-                TaskRecord task = tasks.get(i);
-                if (task.mResizeable) {
-                    if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
-                        // For freeform stack we don't adjust the size of the tasks to match that
-                        // of the stack, but we do try to make sure the tasks are still contained
-                        // with the bounds of the stack.
-                        tempRect2.set(task.mBounds);
-                        fitWithinBounds(tempRect2, bounds);
-                        task.updateOverrideConfiguration(tempRect2);
-                    } else {
-                        task.updateOverrideConfiguration(bounds);
-                    }
-                }
-
-                mTmpConfigs.put(task.taskId, task.mOverrideConfig);
-                mTmpBounds.put(task.taskId, task.mBounds);
-            }
-            stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
-            if (stack.mStackId == DOCKED_STACK_ID) {
-                // Dock stack funness...Yay!
-                if (stack.mFullscreen) {
-                    // The dock stack went fullscreen which is kinda like dismissing it.
-                    // In this case we make all other static stacks fullscreen and move all
-                    // docked stack tasks to the fullscreen stack.
-                    for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                        if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
-                            resizeStackLocked(i, null, preserveWindows, true);
-                        }
-                    }
-
-                    final int count = tasks.size();
-                    for (int i = 0; i < count; i++) {
-                        moveTaskToStackLocked(tasks.get(i).taskId,
-                                FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
-                                false /* animate */);
-                    }
-
-                    // stack shouldn't contain anymore activities, so nothing to resume.
-                    r = null;
-                } else {
-                    // Docked stacks occupy a dedicated region on screen so the size of all other
-                    // static stacks need to be adjusted so they don't overlap with the docked stack.
-                    // We get the bounds to use from window manager which has been adjusted for any
-                    // screen controls and is also the same for all stacks.
-                    mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
-
-                    for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                        if (StackId.isResizeableByDockedStack(i)) {
-                            ActivityStack otherStack = getStack(i);
-                            if (otherStack != null) {
-                                resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
-                            }
-                        }
-                    }
-                }
-                // Since we are resizing the stack, all other operations should strive to preserve
-                // windows.
-                preserveWindows = true;
-            }
-            stack.setBounds(bounds);
-
-            if (r != null) {
-                final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
-                // And we need to make sure at this point that all other activities
-                // are made visible with the correct configuration.
-                ensureActivitiesVisibleLocked(r, 0, preserveWindows);
-                if (!updated) {
-                    resumeTopActivitiesLocked(stack, null, null);
-                }
-            }
+            resizeStackUncheckedLocked(stack, bounds, tempTaskBounds, tempTaskInsetBounds);
+            ensureConfigurationAndResume(stack, stack.topRunningActivityLocked(), preserveWindows);
         } finally {
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
         }
     }
 
-    void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
+    private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds) {
+        if (bounds != null && mWindowManager.isFullscreenBounds(stack.mStackId, bounds)) {
+            // The bounds passed in corresponds to the fullscreen bounds which we normally
+            // represent with null. Go ahead and set it to null so that all tasks configuration
+            // can have the right fullscreen state.
+            bounds = null;
+        }
+        bounds = TaskRecord.validateBounds(bounds);
+
+        mTmpBounds.clear();
+        mTmpConfigs.clear();
+        mTmpInsetBounds.clear();
+        ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        for (int i = tasks.size() - 1; i >= 0; i--) {
+            TaskRecord task = tasks.get(i);
+            if (task.mResizeable) {
+                if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+                    // For freeform stack we don't adjust the size of the tasks to match that
+                    // of the stack, but we do try to make sure the tasks are still contained
+                    // with the bounds of the stack.
+                    tempRect2.set(task.mBounds);
+                    fitWithinBounds(tempRect2, bounds);
+                    task.updateOverrideConfiguration(tempRect2);
+                } else {
+                    task.updateOverrideConfiguration(
+                            tempTaskBounds != null ? tempTaskBounds : bounds);
+                }
+            }
+
+            mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+            mTmpBounds.put(task.taskId, task.mBounds);
+            if (tempTaskInsetBounds != null) {
+                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+            }
+        }
+
+        // We might trigger a configuration change. Save the current task bounds for freezing.
+        mWindowManager.prepareFreezingTaskBounds(stack.mStackId);
+        stack.mFullscreen = mWindowManager.resizeStack(stack.mStackId, bounds, mTmpConfigs,
+                mTmpBounds, mTmpInsetBounds);
+        stack.setBounds(bounds);
+    }
+
+    private void ensureConfigurationAndResume(ActivityStack stack, ActivityRecord r,
+            boolean preserveWindows) {
+        if (r == null) {
+            return;
+        }
+        final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
+                preserveWindows);
+        // And we need to make sure at this point that all other activities
+        // are made visible with the correct configuration.
+        ensureActivitiesVisibleLocked(r, 0, preserveWindows);
+        if (!updated) {
+            resumeFocusedStackTopActivityLocked();
+        }
+    }
+
+    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
+        final ActivityStack stack = getStack(DOCKED_STACK_ID);
+        if (stack == null) {
+            Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
+            return;
+        }
+
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
+        mWindowManager.deferSurfaceLayout();
+        try {
+            ActivityRecord r = stack.topRunningActivityLocked();
+            resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
+                    tempDockedTaskInsetBounds);
+
+            if (stack.mFullscreen) {
+                // The dock stack went fullscreen which is kinda like dismissing it.
+                // In this case we make all other static stacks fullscreen and move all
+                // docked stack tasks to the fullscreen stack.
+                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                    if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
+                        resizeStackLocked(i, null, null, null, preserveWindows,
+                                true /* allowResizeInDockedMode */);
+                    }
+                }
+
+                ArrayList<TaskRecord> tasks = stack.getAllTasks();
+                final int count = tasks.size();
+                for (int i = 0; i < count; i++) {
+                    moveTaskToStackLocked(tasks.get(i).taskId,
+                            FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
+                            false /* animate */);
+                }
+
+                // stack shouldn't contain anymore activities, so nothing to resume.
+                r = null;
+            } else {
+                // Docked stacks occupy a dedicated region on screen so the size of all other
+                // static stacks need to be adjusted so they don't overlap with the docked stack.
+                // We get the bounds to use from window manager which has been adjusted for any
+                // screen controls and is also the same for all stacks.
+                mWindowManager.getStackDockedModeBounds(
+                        HOME_STACK_ID, tempRect, true /* ignoreVisibilityOnKeyguardShowing */);
+                for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+                    if (StackId.isResizeableByDockedStack(i)) {
+                        ActivityStack otherStack = getStack(i);
+                        if (otherStack != null) {
+                            resizeStackLocked(i, tempRect, tempOtherTaskBounds,
+                                    tempOtherTaskInsetBounds, preserveWindows,
+                                    true /* allowResizeInDockedMode */);
+                        }
+                    }
+                }
+            }
+            ensureConfigurationAndResume(stack, r, preserveWindows);
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+
+        mResizeDockedStackTimeout.notifyResizing(dockedBounds,
+                tempDockedTaskBounds != null
+                || tempDockedTaskInsetBounds != null
+                || tempOtherTaskBounds != null
+                || tempOtherTaskInsetBounds != null);
+    }
+
+    boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
         if (!task.mResizeable) {
             Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
-            return;
+            return true;
         }
 
         adjustForMinimalTaskDimensions(task, bounds);
@@ -1927,8 +2003,9 @@
         final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
         if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
             // Nothing to do here...
-            return;
+            return true;
         }
+        bounds = TaskRecord.validateBounds(bounds);
 
         if (!mWindowManager.isValidTaskId(task.taskId)) {
             // Task doesn't exist in window manager yet (e.g. was restored from recents).
@@ -1939,7 +2016,7 @@
                 // re-restore the task so it can have the proper stack association.
                 restoreRecentTaskLocked(task, FREEFORM_WORKSPACE_STACK_ID);
             }
-            return;
+            return true;
         }
 
         // Do not move the task to another stack here.
@@ -1961,13 +2038,14 @@
                 // All other activities must be made visible with their correct configuration.
                 ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
                 if (!kept) {
-                    resumeTopActivitiesLocked(stack, null, null);
+                    resumeFocusedStackTopActivityLocked();
                 }
             }
         }
         mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig, kept, forced);
 
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        return kept;
     }
 
     private void adjustForMinimalTaskDimensions(TaskRecord task, Rect bounds) {
@@ -2083,8 +2161,14 @@
     ActivityStack moveTaskToStackUncheckedLocked(
             TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
         final ActivityRecord r = task.getTopActivity();
-        final boolean wasFocused = isFocusedStack(task.stack) && (topRunningActivityLocked() == r);
-        final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r);
+        final ActivityStack prevStack = task.stack;
+        final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
+        final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+        // In some cases the focused stack isn't the front stack. E.g. pinned stack.
+        // Whenever we are moving the top activity from the front stack we want to make sure to move
+        // the stack to the front.
+        final boolean wasFront = isFrontStack(prevStack)
+                && (prevStack.topRunningActivityLocked() == r);
 
         final boolean resizeable = task.mResizeable;
         // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
@@ -2097,9 +2181,9 @@
         stack.addTask(task, toTop, reason);
 
         // If the task had focus before (or we're requested to move focus),
-        // move focus to the new stack.
-        stack.setFocusAndResumeStateIfNeeded(
-                r, forceFocus || wasFocused, wasResumed, reason);
+        // move focus to the new stack by moving the stack to the front.
+        stack.moveToFrontAndResumeStateIfNeeded(
+                r, forceFocus || wasFocused || wasFront, wasResumed, reason);
 
         return stack;
     }
@@ -2119,13 +2203,17 @@
         }
 
         final ActivityRecord topActivity = task.getTopActivity();
-        if (StackId.preserveWindowOnTaskMove(stackId) && topActivity != null) {
+        final boolean mightReplaceWindow =
+                StackId.preserveWindowOnTaskMove(stackId) && topActivity != null;
+        if (mightReplaceWindow) {
             // We are about to relaunch the activity because its configuration changed due to
             // being maximized, i.e. size change. The activity will first remove the old window
             // and then add a new one. This call will tell window manager about this, so it can
             // preserve the old window until the new one is drawn. This prevents having a gap
             // between the removal and addition, in which no window is visible. We also want the
             // entrance of the new window to be properly animated.
+            // Note here we always set the replacing window first, as the flags might be needed
+            // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
             mWindowManager.setReplacingWindow(topActivity.appToken, animate);
         }
         final ActivityStack stack = moveTaskToStackUncheckedLocked(
@@ -2135,21 +2223,29 @@
             stack.mNoAnimActivities.add(topActivity);
         }
 
+        boolean kept = true;
         // Make sure the task has the appropriate bounds/size for the stack it is in.
         if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
-            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
+            kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         } else if (stackId == FREEFORM_WORKSPACE_STACK_ID
                 && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
-            resizeTaskLocked(task, task.mLastNonFullscreenBounds,
+            kept = resizeTaskLocked(task, task.mLastNonFullscreenBounds,
                     RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
-            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
+            kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
+        }
+
+        if (mightReplaceWindow) {
+            // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
+            // window), we need to clear the replace window settings. Otherwise, we schedule a
+            // timeout to remove the old window if the replacing window is not coming in time.
+            mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeTopActivitiesLocked();
+        resumeFocusedStackTopActivityLocked();
 
         if (!task.mResizeable && isStackDockedInEffect(stackId)) {
             showNonResizeableDockToast(taskId);
@@ -2170,7 +2266,7 @@
             return false;
         }
 
-        if (!mService.mForceResizableActivities && !r.info.supportsPip) {
+        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
             Slog.w(TAG,
                     "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
                             + " r=" + r);
@@ -2194,13 +2290,18 @@
         }
 
         if (bounds != null) {
-            resizeStackLocked(stackId, bounds, !PRESERVE_WINDOWS, true);
+            resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
+                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeTopActivitiesLocked();
+        resumeFocusedStackTopActivityLocked();
+
+        if (stackId == PINNED_STACK_ID) {
+            mService.notifyActivityPinnedLocked();
+        }
     }
 
     void positionTaskInStackLocked(int taskId, int stackId, int position) {
@@ -2219,7 +2320,7 @@
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeTopActivitiesLocked();
+        resumeFocusedStackTopActivityLocked();
     }
 
     ActivityRecord findTaskLocked(ActivityRecord r) {
@@ -2329,7 +2430,7 @@
                 final ActivityStack stack = stacks.get(stackNdx);
                 stack.awakeFromSleepingLocked();
                 if (isFocusedStack(stack)) {
-                    resumeTopActivitiesLocked();
+                    resumeFocusedStackTopActivityLocked();
                 }
             }
         }
@@ -2654,7 +2755,7 @@
         for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
             ActivityRecord s = mStoppingActivities.get(activityNdx);
             final boolean waitingVisible = mWaitingVisibleActivities.contains(s);
-            if (DEBUG_ALL) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
                     + " waitingVisible=" + waitingVisible + " finishing=" + s.finishing);
             if (waitingVisible && nowVisible) {
                 mWaitingVisibleActivities.remove(s);
@@ -2664,12 +2765,12 @@
                     // so get rid of it.  Otherwise, we need to go through the
                     // normal flow and hide it once we determine that it is
                     // hidden by the activities in front of it.
-                    if (DEBUG_ALL) Slog.v(TAG, "Before stopping, can hide: " + s);
+                    if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
                     mWindowManager.setAppVisibility(s.appToken, false);
                 }
             }
             if ((!waitingVisible || mService.isSleepingOrShuttingDown()) && remove) {
-                if (DEBUG_ALL) Slog.v(TAG, "Ready to stop: " + s);
+                if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
                 if (stops == null) {
                     stops = new ArrayList<>();
                 }
@@ -2728,7 +2829,8 @@
         pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
                 pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
         pw.print(prefix); pw.println("mSleepTimeout=" + mSleepTimeout);
-        pw.print(prefix); pw.println("mCurTaskId=" + mCurTaskId);
+        pw.print(prefix);
+        pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
         pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
         pw.print(prefix); pw.println("mActivityContainers=" + mActivityContainers);
         pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString());
@@ -3124,7 +3226,7 @@
                     if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
                             "setLockTaskModeLocked: Tasks remaining, can't unlock");
                     lockedTask.performClearTaskLocked();
-                    resumeTopActivitiesLocked();
+                    resumeFocusedStackTopActivityLocked();
                     return;
                 }
             }
@@ -3165,7 +3267,7 @@
 
         if (andResume) {
             findTaskToMoveToFrontLocked(task, 0, null, reason);
-            resumeTopActivitiesLocked();
+            resumeFocusedStackTopActivityLocked();
         }
     }
 
@@ -3233,7 +3335,7 @@
             didSomething = true;
         }
         if (didSomething) {
-            resumeTopActivitiesLocked();
+            resumeFocusedStackTopActivityLocked();
         }
     }
 
@@ -3241,6 +3343,14 @@
         return mLockTaskModeState;
     }
 
+    void activityRelaunchedLocked(IBinder token) {
+        mWindowManager.notifyAppRelaunchingFinished(token);
+    }
+
+    void activityRelaunchingLocked(ActivityRecord r) {
+        mWindowManager.notifyAppRelaunching(r.appToken);
+    }
+
     void logStackState() {
         mActivityMetricsLogger.logWindowState();
     }
@@ -3280,7 +3390,7 @@
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
                     synchronized (mService) {
-                        resumeTopActivitiesLocked();
+                        resumeFocusedStackTopActivityLocked();
                     }
                 } break;
                 case SLEEP_TIMEOUT_MSG: {
@@ -3661,7 +3771,7 @@
 
             mSurface = surface;
             if (surface != null) {
-                mStack.resumeTopActivityLocked(null);
+                resumeFocusedStackTopActivityLocked();
             } else {
                 mContainerState = CONTAINER_STATE_NO_SURFACE;
                 ((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b16e160..58c14f1 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,17 +1,51 @@
 package com.android.server.am;
 
+import static android.app.Activity.RESULT_CANCELED;
+import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
+import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
+import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
+import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.EXTRA_INTENT;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_TASK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -26,12 +60,13 @@
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
 
-import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
@@ -42,7 +77,6 @@
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -67,7 +101,6 @@
 
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.widget.LockPatternUtils;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
 import com.android.server.wm.WindowManagerService;
 
@@ -79,7 +112,7 @@
  * This class collects all the logic for determining how an intent and flags should be turned into
  * an activity and associated task and stack.
  */
-public class ActivityStarter {
+class ActivityStarter {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_AM;
     private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
@@ -92,6 +125,79 @@
 
     final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
 
+    // Share state variable among methods when starting an activity.
+    private ActivityRecord mStartActivity;
+    private Intent mIntent;
+    private int mCallingUid;
+    private ActivityOptions mOptions;
+
+    private boolean mLaunchSingleTop;
+    private boolean mLaunchSingleInstance;
+    private boolean mLaunchSingleTask;
+    private boolean mLaunchTaskBehind;
+    private int mLaunchFlags;
+
+    private Rect mLaunchBounds;
+
+    private ActivityRecord mNotTop;
+    private boolean mDoResume;
+    private int mStartFlags;
+    private ActivityRecord mSourceRecord;
+
+    private TaskRecord mInTask;
+    private boolean mAddingToTask;
+    private TaskRecord mReuseTask;
+
+    private ActivityInfo mNewTaskInfo;
+    private Intent mNewTaskIntent;
+    private ActivityStack mSourceStack;
+    private ActivityStack mTargetStack;
+    // TODO: Is the mMoveHome flag really needed?
+    private boolean mMovedHome;
+    private boolean mMovedToFront;
+    private boolean mNoAnimation;
+    private boolean mKeepCurTransition;
+
+    private IVoiceInteractionSession mVoiceSession;
+    private IVoiceInteractor mVoiceInteractor;
+
+    private void reset() {
+        mStartActivity = null;
+        mIntent = null;
+        mCallingUid = -1;
+        mOptions = null;
+
+        mLaunchSingleTop = false;
+        mLaunchSingleInstance = false;
+        mLaunchSingleTask = false;
+        mLaunchTaskBehind = false;
+        mLaunchFlags = 0;
+
+        mLaunchBounds = null;
+
+        mNotTop = null;
+        mDoResume = false;
+        mStartFlags = 0;
+        mSourceRecord = null;
+
+        mInTask = null;
+        mAddingToTask = false;
+        mReuseTask = null;
+
+        mNewTaskInfo = null;
+        mNewTaskIntent = null;
+        mSourceStack = null;
+
+        mTargetStack = null;
+        mMovedHome = false;
+        mMovedToFront = false;
+        mNoAnimation = false;
+        mKeepCurTransition = false;
+
+        mVoiceSession = null;
+        mVoiceInteractor = null;
+    }
+
     ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
         mService = service;
         mSupervisor = supervisor;
@@ -234,11 +340,10 @@
 
         final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
 
-        if (err != ActivityManager.START_SUCCESS) {
+        if (err != START_SUCCESS) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1,
-                        resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
+                resultStack.sendActivityResultLocked(
+                        -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
             }
             ActivityOptions.abort(options);
             return err;
@@ -262,48 +367,36 @@
             }
         }
 
-        UserInfo user = mSupervisor.getUserInfo(userId);
-        KeyguardManager km = (KeyguardManager) mService.mContext
-                .getSystemService(Context.KEYGUARD_SERVICE);
-        if (user.isManagedProfile()
-                && LockPatternUtils.isSeparateWorkChallengeEnabled()
-                && km.isDeviceLocked(userId)) {
-            IIntentSender target = mService.getIntentSenderLocked(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                    Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
-                    new String[]{ resolvedType },
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
-                            | PendingIntent.FLAG_IMMUTABLE, null);
-            final int flags = intent.getFlags();
-            final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
-            if (newIntent != null) {
-                intent = newIntent;
-                intent.setFlags(flags
-                        | Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
-                intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-
-                resolvedType = null;
-                callingUid = realCallingUid;
-                callingPid = realCallingPid;
-
-                UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
-                rInfo = mSupervisor.resolveIntent(intent, resolvedType, parent.id);
-                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
-                        null /*profilerInfo*/);
+        final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(intent,
+                resolvedType, aInfo, callingPackage, userId);
+        if (interceptingIntent != null) {
+            intent = interceptingIntent;
+            callingPid = realCallingPid;
+            callingUid = realCallingUid;
+            resolvedType = null;
+            // If we are intercepting and there was a task, convert it into an extra for the
+            // ConfirmCredentials intent and unassign it, as otherwise the task will move to
+            // front even if ConfirmCredentials is cancelled.
+            if (inTask != null) {
+                intent.putExtra(EXTRA_TASK_ID, inTask.taskId);
+                inTask = null;
             }
+
+            final UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
+            rInfo = mSupervisor.resolveIntent(intent, resolvedType, parent.id);
+            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
+                    null /*profilerInfo*/);
         }
 
         if (abort) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
+                        RESULT_CANCELED, null);
             }
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
             ActivityOptions.abort(options);
-            return ActivityManager.START_SUCCESS;
+            return START_SUCCESS;
         }
 
         // If permissions need a review before any of the app components can run, we
@@ -429,8 +522,8 @@
 
         doPendingActivityLaunchesLocked(false);
 
-        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession,
-                voiceInteractor, startFlags, true, options, inTask);
+        err = startActivityUnchecked(
+                r, sourceRecord, voiceSession, voiceInteractor, startFlags, true, options, inTask);
 
         if (err < 0) {
             // If someone asked to have the keyguard dismissed on the next
@@ -438,10 +531,41 @@
             // switch...  just dismiss the keyguard now, because we
             // probably want to see whatever is behind it.
             mSupervisor.notifyActivityDrawnForKeyguard();
+        } else {
+            launchRecentsAppIfNeeded(stack);
         }
+
         return err;
     }
 
+    /**
+     * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
+     *
+     * @return The intercepting intent if needed.
+     */
+    private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType,
+            ActivityInfo aInfo, String callingPackage, int userId) {
+        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+            return null;
+        }
+        final IIntentSender target = mService.getIntentSenderLocked(
+                INTENT_SENDER_ACTIVITY, callingPackage,
+                Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+                new String[]{ resolvedType },
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
+        final int flags = intent.getFlags();
+        final KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(KEYGUARD_SERVICE);
+        final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
+        if (newIntent == null) {
+            return null;
+        }
+        newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
+        newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
+        return newIntent;
+    }
+
     void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
         mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
         startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
@@ -608,7 +732,7 @@
                         } catch (InterruptedException e) {
                         }
                     } while (!outResult.timeout && outResult.who == null);
-                } else if (res == ActivityManager.START_TASK_TO_FRONT) {
+                } else if (res == START_TASK_TO_FRONT) {
                     ActivityRecord r = stack.topRunningActivityLocked();
                     if (r.nowVisible && r.state == RESUMED) {
                         outResult.timeout = false;
@@ -632,682 +756,6 @@
         }
     }
 
-    final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
-            boolean doResume, ActivityOptions options, TaskRecord inTask) {
-        final Intent intent = r.intent;
-        final int callingUid = r.launchedFromUid;
-
-        final Rect newBounds = mSupervisor.getOverrideBounds(r, options, inTask);
-        final boolean overrideBounds = newBounds != null;
-
-        // In some flows in to this function, we retrieve the task record and hold on to it
-        // without a lock before calling back in to here...  so the task at this point may
-        // not actually be in recents.  Check for that, and if it isn't in recents just
-        // consider it invalid.
-        if (inTask != null && !inTask.inRecents) {
-            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
-            inTask = null;
-        }
-
-        final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
-        final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-        final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
-        int launchFlags = adjustLaunchFlagsToDocumentMode(r, launchSingleInstance, launchSingleTask,
-                intent.getFlags());
-        final boolean launchTaskBehind = r.mLaunchTaskBehind
-                && !launchSingleTask && !launchSingleInstance
-                && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
-
-        if (r.resultTo != null && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0
-                && r.resultTo.task.stack != null) {
-            // For whatever reason this activity is being launched into a new
-            // task...  yet the caller has requested a result back.  Well, that
-            // is pretty messed up, so instead immediately send back a cancel
-            // and let the new task continue launched as normal without a
-            // dependency on its originator.
-            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            r.resultTo.task.stack.sendActivityResultLocked(-1,
-                    r.resultTo, r.resultWho, r.requestCode,
-                    Activity.RESULT_CANCELED, null);
-            r.resultTo = null;
-        }
-
-        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
-            launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-        }
-
-        // If we are actually going to launch in to a new task, there are some cases where
-        // we further want to do multiple task.
-        if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            if (launchTaskBehind
-                    || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
-                launchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
-            }
-        }
-
-        // We'll invoke onUserLeaving before onPause only if the launching
-        // activity did not explicitly state that this is an automated launch.
-        mSupervisor.mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
-        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
-                "startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving);
-
-        // If the caller has asked not to resume at this point, we make note
-        // of this in the record so that we can skip it when trying to find
-        // the top running activity.
-        if (!doResume || !mSupervisor.okToShowLocked(r)) {
-            r.delayedResume = true;
-            doResume = false;
-        }
-
-        final ActivityRecord notTop =
-                (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
-
-        // If the onlyIfNeeded flag is set, then we can do this if the activity
-        // being launched is the same as the one making the call...  or, as
-        // a special case, if we do not know the caller then we count the
-        // current top activity as the caller.
-        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-            ActivityRecord checkedCaller = sourceRecord;
-            if (checkedCaller == null) {
-                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
-                        notTop);
-            }
-            if (!checkedCaller.realActivity.equals(r.realActivity)) {
-                // Caller is not the same as launcher, so always needed.
-                startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
-            }
-        }
-
-        boolean addingToTask = false;
-        TaskRecord reuseTask = null;
-
-        // If the caller is not coming from another activity, but has given us an
-        // explicit task into which they would like us to launch the new activity,
-        // then let's see about doing that.
-        if (sourceRecord == null && inTask != null && inTask.stack != null) {
-            final Intent baseIntent = inTask.getBaseIntent();
-            final ActivityRecord root = inTask.getRootActivity();
-            if (baseIntent == null) {
-                ActivityOptions.abort(options);
-                throw new IllegalArgumentException("Launching into task without base intent: "
-                        + inTask);
-            }
-
-            // If this task is empty, then we are adding the first activity -- it
-            // determines the root, and must be launching as a NEW_TASK.
-            if (launchSingleInstance || launchSingleTask) {
-                if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
-                    ActivityOptions.abort(options);
-                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
-                            + r + " into different task " + inTask);
-                }
-                if (root != null) {
-                    ActivityOptions.abort(options);
-                    throw new IllegalArgumentException("Caller with inTask " + inTask
-                            + " has root " + root + " but target is singleInstance/Task");
-                }
-            }
-
-            // If task is empty, then adopt the interesting intent launch flags in to the
-            // activity being started.
-            if (root == null) {
-                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK
-                        | FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
-                        | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
-                launchFlags = (launchFlags&~flagsOfInterest)
-                        | (baseIntent.getFlags()&flagsOfInterest);
-                intent.setFlags(launchFlags);
-                inTask.setIntent(r);
-                addingToTask = true;
-
-                // If the task is not empty and the caller is asking to start it as the root
-                // of a new task, then we don't actually want to start this on the task.  We
-                // will bring the task to the front, and possibly give it a new intent.
-            } else if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-                addingToTask = false;
-
-            } else {
-                addingToTask = true;
-            }
-
-            reuseTask = inTask;
-        } else {
-            inTask = null;
-            // Launch ResolverActivity in the source task, so that it stays in the task
-            // bounds when in freeform workspace.
-            // Also put noDisplay activities in the source task. These by itself can
-            // be placed in any task/stack, however it could launch other activities
-            // like ResolverActivity, and we want those to stay in the original task.
-            if ((r.isResolverActivity() || r.noDisplay) && sourceRecord != null
-                    && sourceRecord.isFreeform())  {
-                addingToTask = true;
-            }
-        }
-
-        if (inTask == null) {
-            if (sourceRecord == null) {
-                // This activity is not being started from another...  in this
-                // case we -always- start a new task.
-                if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
-                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
-                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
-                    launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-                }
-            } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                // The original activity who is starting us is running as a single
-                // instance...  this new activity it is starting must go on its
-                // own task.
-                launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            } else if (launchSingleInstance || launchSingleTask) {
-                // The activity being started is a single instance...  it always
-                // gets launched into its own task.
-                launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-            }
-        }
-
-        ActivityInfo newTaskInfo = null;
-        Intent newTaskIntent = null;
-        final ActivityStack sourceStack;
-        if (sourceRecord != null) {
-            if (sourceRecord.finishing) {
-                // If the source is finishing, we can't further count it as our source.  This
-                // is because the task it is associated with may now be empty and on its way out,
-                // so we don't want to blindly throw it in to that task.  Instead we will take
-                // the NEW_TASK flow and try to find a task for it. But save the task information
-                // so it can be used when creating the new task.
-                if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
-                    Slog.w(TAG, "startActivity called from finishing " + sourceRecord
-                            + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
-                    launchFlags |= FLAG_ACTIVITY_NEW_TASK;
-                    newTaskInfo = sourceRecord.info;
-                    newTaskIntent = sourceRecord.task.intent;
-                }
-                sourceRecord = null;
-                sourceStack = null;
-            } else {
-                sourceStack = sourceRecord.task.stack;
-            }
-        } else {
-            sourceStack = null;
-        }
-
-        boolean movedHome = false;
-        ActivityStack targetStack;
-
-        intent.setFlags(launchFlags);
-        final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
-
-        ActivityRecord intentActivity = getReusableIntentActivity(r, inTask, intent,
-                launchSingleInstance, launchSingleTask, launchFlags);
-        if (intentActivity != null) {
-            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused
-            // but still needs to be a lock task mode violation since the task gets
-            // cleared out and the device would otherwise leave the locked task.
-            if (mSupervisor.isLockTaskModeViolation(intentActivity.task,
-                    (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
-                mSupervisor.showLockTaskToast();
-                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            if (r.task == null) {
-                r.task = intentActivity.task;
-            }
-            if (intentActivity.task.intent == null) {
-                // This task was started because of movement of the activity based on affinity...
-                // Now that we are actually launching it, we can assign the base intent.
-                intentActivity.task.setIntent(r);
-            }
-
-            targetStack = intentActivity.task.stack;
-            targetStack.mLastPausedActivity = null;
-            // If the target task is not in the front, then we need
-            // to bring it to the front...  except...  well, with
-            // SINGLE_TASK_LAUNCH it's not entirely clear.  We'd like
-            // to have the same behavior as if a new instance was
-            // being started, which means not bringing it to the front
-            // if the caller is not itself in the front.
-            final ActivityStack focusStack = mSupervisor.getFocusedStack();
-            ActivityRecord curTop = (focusStack == null)
-                    ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
-            boolean movedToFront = false;
-            if (curTop != null && (curTop.task != intentActivity.task ||
-                    curTop.task != focusStack.topTask())) {
-                r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-                if (sourceRecord == null || (sourceStack.topActivity() != null &&
-                        sourceStack.topActivity().task == sourceRecord.task)) {
-                    // We really do want to push this one into the user's face, right now.
-                    if (launchTaskBehind && sourceRecord != null) {
-                        intentActivity.setTaskToAffiliateWith(sourceRecord.task);
-                    }
-                    movedHome = true;
-                    final ActivityStack sideStack = getLaunchToSideStack(r, launchFlags, r.task);
-                    if (sideStack == null || sideStack == targetStack) {
-                        // We only want to move to the front, if we aren't going to launch on a
-                        // different stack. If we launch on a different stack, we will put the
-                        // task on top there.
-                        targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
-                                options, r.appTimeTracker, "bringingFoundTaskToFront");
-                        movedToFront = true;
-                    }
-                    if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
-                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
-                        // Caller wants to appear on home activity.
-                        intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-                    }
-                    options = null;
-                }
-            }
-            if (!movedToFront && doResume) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack
-                        + " from " + intentActivity);
-                targetStack.moveToFront("intentActivityFound");
-            }
-
-            // If the caller has requested that the target task be
-            // reset, then do so.
-            if ((launchFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
-                intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
-            }
-            if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-                // We don't need to start a new activity, and
-                // the client said not to do anything if that
-                // is the case, so this is it!  And for paranoia, make
-                // sure we have correctly resumed the top activity.
-                if (doResume) {
-                    mSupervisor.resumeTopActivitiesLocked(targetStack, null, options);
-
-                    // Make sure to notify Keyguard as well if we are not running an app
-                    // transition later.
-                    if (!movedToFront) {
-                        mSupervisor.notifyActivityDrawnForKeyguard();
-                    }
-                } else {
-                    ActivityOptions.abort(options);
-                }
-                mSupervisor.updateUserStackLocked(r.userId, targetStack);
-                return ActivityManager.START_RETURN_INTENT_TO_CALLER;
-            }
-            if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
-                    == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
-                // The caller has requested to completely replace any
-                // existing task with its new activity.  Well that should
-                // not be too hard...
-                reuseTask = intentActivity.task;
-                reuseTask.performClearTaskLocked();
-                reuseTask.setIntent(r);
-            } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
-                    || launchSingleInstance || launchSingleTask) {
-                // In this situation we want to remove all activities
-                // from the task up to the one being started.  In most
-                // cases this means we are resetting the task to its
-                // initial state.
-                ActivityRecord top = intentActivity.task.performClearTaskLocked(r, launchFlags);
-                if (top != null) {
-                    if (top.frontOfTask) {
-                        // Activity aliases may mean we use different
-                        // intents for the top activity, so make sure
-                        // the task now has the identity of the new
-                        // intent.
-                        top.task.setIntent(r);
-                    }
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                } else {
-                    // A special case: we need to start the activity because it is not
-                    // currently running, and the caller has asked to clear the current
-                    // task to have this activity at the top.
-                    addingToTask = true;
-                    // Now pretend like this activity is being started by the top of its
-                    // task, so it is put in the right place.
-                    sourceRecord = intentActivity;
-                    TaskRecord task = sourceRecord.task;
-                    if (task != null && task.stack == null) {
-                        // Target stack got cleared when we all activities were removed
-                        // above. Go ahead and reset it.
-                        targetStack = computeStackFocus(
-                                sourceRecord, false /* newTask */, null /* bounds */, launchFlags);
-                        targetStack.addTask(task,
-                                !launchTaskBehind /* toTop */, "startActivityUnchecked");
-                    }
-
-                }
-            } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
-                // In this case the top activity on the task is the
-                // same as the one being launched, so we take that
-                // as a request to bring the task to the foreground.
-                // If the top activity in the task is the root
-                // activity, deliver this new intent to it if it
-                // desires.
-                if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
-                        && intentActivity.realActivity.equals(r.realActivity)) {
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
-                            intentActivity.task);
-                    if (intentActivity.frontOfTask) {
-                        intentActivity.task.setIntent(r);
-                    }
-                    intentActivity.deliverNewIntentLocked(callingUid, r.intent,
-                            r.launchedFromPackage);
-                } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
-                    // In this case we are launching the root activity
-                    // of the task, but with a different intent.  We
-                    // should start a new instance on top.
-                    addingToTask = true;
-                    sourceRecord = intentActivity;
-                }
-            } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
-                // In this case an activity is being launched in to an
-                // existing task, without resetting that task.  This
-                // is typically the situation of launching an activity
-                // from a notification or shortcut.  We want to place
-                // the new activity on top of the current task.
-                addingToTask = true;
-                sourceRecord = intentActivity;
-            } else if (!intentActivity.task.rootWasReset) {
-                // In this case we are launching in to an existing task
-                // that has not yet been started from its front door.
-                // The current task has been brought to the front.
-                // Ideally, we'd probably like to place this new task
-                // at the bottom of its stack, but that's a little hard
-                // to do with the current organization of the code so
-                // for now we'll just drop it.
-                intentActivity.task.setIntent(r);
-            }
-            if (!addingToTask && reuseTask == null) {
-                // We didn't do anything...  but it was needed (a.k.a., client
-                // don't use that intent!)  And for paranoia, make
-                // sure we have correctly resumed the top activity.
-                if (doResume) {
-                    targetStack.resumeTopActivityLocked(null, options);
-                    if (!movedToFront) {
-                        // Make sure to notify Keyguard as well if we are not running an app
-                        // transition later.
-                        mSupervisor.notifyActivityDrawnForKeyguard();
-                    }
-                } else {
-                    ActivityOptions.abort(options);
-                }
-                mSupervisor.updateUserStackLocked(r.userId, targetStack);
-                return ActivityManager.START_TASK_TO_FRONT;
-            }
-        }
-
-        //String uri = r.intent.toURI();
-        //Intent intent2 = new Intent(uri);
-        //Slog.i(TAG, "Given intent: " + r.intent);
-        //Slog.i(TAG, "URI is: " + uri);
-        //Slog.i(TAG, "To intent: " + intent2);
-
-        if (r.packageName != null) {
-            // If the activity being launched is the same as the one currently
-            // at the top, then we need to check if it should only be launched
-            // once.
-            ActivityStack topStack = mSupervisor.mFocusedStack;
-            ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
-            final boolean dontStart = top != null && r.resultTo == null
-                    && top.realActivity.equals(r.realActivity) && top.userId == r.userId
-                    && top.app != null && top.app.thread != null
-                    && ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                    || launchSingleTop || launchSingleTask);
-            if (dontStart) {
-                ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
-                // For paranoia, make sure we have correctly resumed the top activity.
-                topStack.mLastPausedActivity = null;
-                if (doResume) {
-                    mSupervisor.resumeTopActivitiesLocked();
-                }
-                ActivityOptions.abort(options);
-                if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-                    // We don't need to start a new activity, and the client said not to do
-                    // anything if that is the case, so this is it!
-                    return ActivityManager.START_RETURN_INTENT_TO_CALLER;
-                }
-                top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                return ActivityManager.START_DELIVERED_TO_TOP;
-            }
-        } else {
-            if (r.resultTo != null && r.resultTo.task.stack != null) {
-                r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
-                        r.requestCode, Activity.RESULT_CANCELED, null);
-            }
-            ActivityOptions.abort(options);
-            return ActivityManager.START_CLASS_NOT_FOUND;
-        }
-
-        boolean newTask = false;
-        boolean keepCurTransition = false;
-
-        final TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
-                sourceRecord.task : null;
-
-        // Should this be considered a new task?
-        if (r.resultTo == null && inTask == null && !addingToTask
-                && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
-            newTask = true;
-            targetStack = computeStackFocus(r, newTask, newBounds, launchFlags);
-            if (doResume) {
-                targetStack.moveToFront("startingNewTask");
-            }
-
-            if (reuseTask == null) {
-                r.setTask(targetStack.createTaskRecord(mSupervisor.getNextTaskId(),
-                        newTaskInfo != null ? newTaskInfo : r.info,
-                        newTaskIntent != null ? newTaskIntent : intent,
-                        voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
-                        taskToAffiliate);
-                if (overrideBounds) {
-                    r.task.updateOverrideConfiguration(newBounds);
-                }
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
-                        "Starting new activity " + r + " in new task " + r.task);
-            } else {
-                r.setTask(reuseTask, taskToAffiliate);
-            }
-            if (mSupervisor.isLockTaskModeViolation(r.task)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            if (!movedHome) {
-                if ((launchFlags &
-                        (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
-                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
-                    // Caller wants to appear on home activity, so before starting
-                    // their own activity we will bring home to the front.
-                    r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-                }
-            }
-        } else if (sourceRecord != null) {
-            final TaskRecord sourceTask = sourceRecord.task;
-            if (mSupervisor.isLockTaskModeViolation(sourceTask)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            targetStack = null;
-            if (sourceTask.stack.topTask() != sourceTask) {
-                // We only want to allow changing stack if the target task is not the top one,
-                // otherwise we would move the launching task to the other side, rather than show
-                // two side by side.
-                targetStack = getLaunchToSideStack(r, launchFlags, r.task);
-            }
-            if (targetStack == null) {
-                targetStack = sourceTask.stack;
-            } else if (targetStack != sourceTask.stack) {
-                mSupervisor.moveTaskToStackLocked(sourceTask.taskId, targetStack.mStackId,
-                        ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE);
-            }
-            if (doResume) {
-                targetStack.moveToFront("sourceStackToFront");
-            }
-            final TaskRecord topTask = targetStack.topTask();
-            if (topTask != sourceTask) {
-                targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
-                        r.appTimeTracker, "sourceTaskToFront");
-            }
-            if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                // In this case, we are adding the activity to an existing
-                // task, but the caller has asked to clear that task if the
-                // activity is already running.
-                ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
-                keepCurTransition = true;
-                if (top != null) {
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                    // For paranoia, make sure we have correctly
-                    // resumed the top activity.
-                    targetStack.mLastPausedActivity = null;
-                    if (doResume) {
-                        targetStack.resumeTopActivityLocked(null);
-                    }
-                    ActivityOptions.abort(options);
-                    return ActivityManager.START_DELIVERED_TO_TOP;
-                }
-            } else if (!addingToTask &&
-                    (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
-                // In this case, we are launching an activity in our own task
-                // that may already be running somewhere in the history, and
-                // we want to shuffle it to the front of the stack if so.
-                final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
-                if (top != null) {
-                    final TaskRecord task = top.task;
-                    task.moveActivityToFrontLocked(top);
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
-                    top.updateOptionsLocked(options);
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                    targetStack.mLastPausedActivity = null;
-                    if (doResume) {
-                        targetStack.resumeTopActivityLocked(null);
-                    }
-                    return ActivityManager.START_DELIVERED_TO_TOP;
-                }
-            }
-            // An existing activity is starting this new activity, so we want
-            // to keep the new one in the same task as the one that is starting
-            // it.
-            r.setTask(sourceTask, null);
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
-                    + " in existing task " + r.task + " from source " + sourceRecord);
-
-        } else if (inTask != null) {
-            // The caller is asking that the new activity be started in an explicit
-            // task it has provided to us.
-            if (mSupervisor.isLockTaskModeViolation(inTask)) {
-                Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
-                return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
-            }
-            if (overrideBounds) {
-                inTask.updateOverrideConfiguration(newBounds);
-                int stackId = inTask.getLaunchStackId();
-                if (stackId != inTask.stack.mStackId) {
-                    mSupervisor.moveTaskToStackUncheckedLocked(
-                            inTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
-                }
-            }
-            targetStack = inTask.stack;
-            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
-                    "inTaskToFront");
-
-            // Check whether we should actually launch the new activity in to the task,
-            // or just reuse the current activity on top.
-            ActivityRecord top = inTask.getTopActivity();
-            if (top != null && top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
-                if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
-                        || launchSingleTop || launchSingleTask) {
-                    ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
-                    if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
-                        // We don't need to start a new activity, and
-                        // the client said not to do anything if that
-                        // is the case, so this is it!
-                        return ActivityManager.START_RETURN_INTENT_TO_CALLER;
-                    }
-                    top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
-                    return ActivityManager.START_DELIVERED_TO_TOP;
-                }
-            }
-
-            if (!addingToTask) {
-                // We don't actually want to have this activity added to the task, so just
-                // stop here but still tell the caller that we consumed the intent.
-                ActivityOptions.abort(options);
-                return ActivityManager.START_TASK_TO_FRONT;
-            }
-
-            r.setTask(inTask, null);
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
-                    + " in explicit task " + r.task);
-
-        } else {
-            // This not being started from an existing activity, and not part
-            // of a new task...  just put it in the top task, though these days
-            // this case should never happen.
-            targetStack = computeStackFocus(r, newTask, null /* bounds */, launchFlags);
-            if (doResume) {
-                targetStack.moveToFront("addingToTopTask");
-            }
-            ActivityRecord prev = targetStack.topActivity();
-            r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(
-                    mSupervisor.getNextTaskId(), r.info, intent, null, null, true), null);
-            mWindowManager.moveTaskToTop(r.task.taskId);
-            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
-                    + " in new guessed " + r.task);
-        }
-
-        mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
-                intent, r.getUriPermissionsLocked(), r.userId);
-
-        if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
-            r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
-        }
-        if (newTask) {
-            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
-        }
-        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
-        targetStack.mLastPausedActivity = null;
-        targetStack.startActivityLocked(r, newTask, keepCurTransition, options);
-        if (doResume) {
-            if (!launchTaskBehind) {
-                mService.setFocusedActivityLocked(r, "startedActivity");
-            }
-            mSupervisor.resumeTopActivitiesLocked(targetStack, r, options);
-        } else {
-            targetStack.addRecentActivityLocked(r);
-        }
-        mSupervisor.updateUserStackLocked(r.userId, targetStack);
-
-        if (!r.task.mResizeable && mSupervisor.isStackDockedInEffect(targetStack.mStackId)) {
-            mSupervisor.showNonResizeableDockToast(r.task.taskId);
-        }
-
-        return ActivityManager.START_SUCCESS;
-    }
-
-    private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
-            boolean launchSingleTask, int launchFlags) {
-        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
-                (launchSingleInstance || launchSingleTask)) {
-            // We have a conflict between the Intent and the Activity manifest, manifest wins.
-            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
-                    "\"singleInstance\" or \"singleTask\"");
-            launchFlags &=
-                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
-        } else {
-            switch (r.info.documentLaunchMode) {
-                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
-                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-                    break;
-                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
-                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
-                    break;
-            }
-        }
-        return launchFlags;
-    }
-
     final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo,
             Bundle bOptions, int userId) {
@@ -1381,7 +829,765 @@
             Binder.restoreCallingIdentity(origId);
         }
 
-        return ActivityManager.START_SUCCESS;
+        return START_SUCCESS;
+    }
+
+    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
+
+        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
+                voiceInteractor);
+
+        computeLaunchingTaskFlags();
+
+        computeSourceStack();
+
+        mIntent.setFlags(mLaunchFlags);
+
+        ActivityRecord intentActivity = getReusableIntentActivity();
+
+        if (intentActivity != null) {
+            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
+            // still needs to be a lock task mode violation since the task gets cleared out and
+            // the device would otherwise leave the locked task.
+            if (mSupervisor.isLockTaskModeViolation(intentActivity.task,
+                    (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
+                mSupervisor.showLockTaskToast();
+                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
+                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+
+            if (mStartActivity.task == null) {
+                mStartActivity.task = intentActivity.task;
+            }
+            if (intentActivity.task.intent == null) {
+                // This task was started because of movement of the activity based on affinity...
+                // Now that we are actually launching it, we can assign the base intent.
+                intentActivity.task.setIntent(mStartActivity);
+            }
+
+            // This code path leads to delivering a new intent, we want to make sure we schedule it
+            // as the first operation, in case the activity will be resumed as a result of later
+            // operations.
+            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+                    || mLaunchSingleInstance || mLaunchSingleTask) {
+                // In this situation we want to remove all activities from the task up to the one
+                // being started. In most cases this means we are resetting the task to its initial
+                // state.
+                final ActivityRecord top = intentActivity.task.performClearTaskLocked(
+                        mStartActivity, mLaunchFlags);
+                if (top != null) {
+                    if (top.frontOfTask) {
+                        // Activity aliases may mean we use different intents for the top activity,
+                        // so make sure the task now has the identity of the new intent.
+                        top.task.setIntent(mStartActivity);
+                    }
+                    ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
+                    top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+                            mStartActivity.launchedFromPackage);
+                }
+            }
+
+            intentActivity = setTargetStackAndMoveToFrontIfNeeded(intentActivity);
+
+            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+                // We don't need to start a new activity, and the client said not to do anything
+                // if that is the case, so this is it!  And for paranoia, make sure we have
+                // correctly resumed the top activity.
+                resumeTargetStackIfNeeded();
+                return START_RETURN_INTENT_TO_CALLER;
+            }
+
+            setTaskFromIntentActivity(intentActivity);
+
+            if (!mAddingToTask && mReuseTask == null) {
+                // We didn't do anything...  but it was needed (a.k.a., client don't use that
+                // intent!)  And for paranoia, make sure we have correctly resumed the top activity.
+                resumeTargetStackIfNeeded();
+                return START_TASK_TO_FRONT;
+            }
+        }
+
+        if (mStartActivity.packageName == null) {
+            if (mStartActivity.resultTo != null && mStartActivity.resultTo.task.stack != null) {
+                mStartActivity.resultTo.task.stack.sendActivityResultLocked(
+                        -1, mStartActivity.resultTo, mStartActivity.resultWho,
+                        mStartActivity.requestCode, RESULT_CANCELED, null);
+            }
+            ActivityOptions.abort(mOptions);
+            return START_CLASS_NOT_FOUND;
+        }
+
+        // If the activity being launched is the same as the one currently at the top, then
+        // we need to check if it should only be launched once.
+        final ActivityStack topStack = mSupervisor.mFocusedStack;
+        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
+        final boolean dontStart = top != null && mStartActivity.resultTo == null
+                && top.realActivity.equals(mStartActivity.realActivity)
+                && top.userId == mStartActivity.userId
+                && top.app != null && top.app.thread != null
+                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
+                || mLaunchSingleTop || mLaunchSingleTask);
+        if (dontStart) {
+            ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
+            // For paranoia, make sure we have correctly resumed the top activity.
+            topStack.mLastPausedActivity = null;
+            if (mDoResume) {
+                mSupervisor.resumeFocusedStackTopActivityLocked();
+            }
+            ActivityOptions.abort(mOptions);
+            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+                // We don't need to start a new activity, and the client said not to do
+                // anything if that is the case, so this is it!
+                return START_RETURN_INTENT_TO_CALLER;
+            }
+            top.deliverNewIntentLocked(
+                    mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+            return START_DELIVERED_TO_TOP;
+        }
+
+        boolean newTask = false;
+        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
+                ? mSourceRecord.task : null;
+
+        // Should this be considered a new task?
+        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
+                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            newTask = true;
+            setTaskFromReuseOrCreateNewTask(taskToAffiliate);
+
+            if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+            if (!mMovedHome
+                    && (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
+                    == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
+                // Caller wants to appear on home activity, so before starting
+                // their own activity we will bring home to the front.
+                mStartActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            }
+        } else if (mSourceRecord != null) {
+            if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+
+            final int result = setTaskFromSourceRecord();
+            if (result != START_SUCCESS) {
+                return result;
+            }
+        } else if (mInTask != null) {
+            // The caller is asking that the new activity be started in an explicit
+            // task it has provided to us.
+            if (mSupervisor.isLockTaskModeViolation(mInTask)) {
+                Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
+                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
+            }
+
+            final int result = setTaskFromInTask();
+            if (result != START_SUCCESS) {
+                return result;
+            }
+        } else {
+            // This not being started from an existing activity, and not part of a new task...
+            // just put it in the top task, though these days this case should never happen.
+            setTaskToCurrentTopOrCreateNewTask();
+        }
+
+        mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
+                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
+
+        if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
+            mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
+        }
+        if (newTask) {
+            EventLog.writeEvent(
+                    EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId);
+        }
+        ActivityStack.logStartActivity(
+                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
+        mTargetStack.mLastPausedActivity = null;
+        mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
+        if (mDoResume) {
+            if (!mLaunchTaskBehind) {
+                // TODO(b/26381750): Remove this code after verification that all the decision
+                // points above moved targetStack to the front which will also set the focus
+                // activity.
+                mService.setFocusedActivityLocked(mStartActivity, "startedActivity");
+            }
+            mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
+                    mOptions);
+        } else {
+            mTargetStack.addRecentActivityLocked(mStartActivity);
+        }
+        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+
+        if (!mStartActivity.task.mResizeable
+                && mSupervisor.isStackDockedInEffect(mTargetStack.mStackId)) {
+            mSupervisor.showNonResizeableDockToast(mStartActivity.task.taskId);
+        }
+
+        return START_SUCCESS;
+    }
+
+    private void launchRecentsAppIfNeeded(ActivityStack topStack) {
+        if (topStack.mStackId == HOME_STACK_ID && mTargetStack.mStackId == DOCKED_STACK_ID) {
+            // We launch an activity while being in home stack, which means either launcher or
+            // recents into docked stack. We don't want the launched activity to be alone in a
+            // docked stack, so we want to immediately launch recents too.
+            if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+            mWindowManager.showRecentApps();
+        }
+    }
+
+    private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
+            boolean doResume, int startFlags, ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+        reset();
+
+        mStartActivity = r;
+        mIntent = r.intent;
+        mOptions = options;
+        mCallingUid = r.launchedFromUid;
+        mSourceRecord = sourceRecord;
+        mVoiceSession = voiceSession;
+        mVoiceInteractor = voiceInteractor;
+
+        mLaunchBounds = getOverrideBounds(r, options, inTask);
+
+        mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
+        mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
+        mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
+        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
+                r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
+        mLaunchTaskBehind = r.mLaunchTaskBehind
+                && !mLaunchSingleTask && !mLaunchSingleInstance
+                && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
+
+        sendNewTaskResultRequestIfNeeded();
+
+        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
+            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+        }
+
+        // If we are actually going to launch in to a new task, there are some cases where
+        // we further want to do multiple task.
+        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+            if (mLaunchTaskBehind
+                    || r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
+                mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
+            }
+        }
+
+        // We'll invoke onUserLeaving before onPause only if the launching
+        // activity did not explicitly state that this is an automated launch.
+        mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;
+        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                "startActivity() => mUserLeaving=" + mSupervisor.mUserLeaving);
+
+        // If the caller has asked not to resume at this point, we make note
+        // of this in the record so that we can skip it when trying to find
+        // the top running activity.
+        mDoResume = doResume;
+        if (!doResume || !mSupervisor.okToShowLocked(r)) {
+            r.delayedResume = true;
+            mDoResume = false;
+        }
+
+        mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
+
+        mInTask = inTask;
+        // In some flows in to this function, we retrieve the task record and hold on to it
+        // without a lock before calling back in to here...  so the task at this point may
+        // not actually be in recents.  Check for that, and if it isn't in recents just
+        // consider it invalid.
+        if (inTask != null && !inTask.inRecents) {
+            Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
+            mInTask = null;
+        }
+
+        mStartFlags = startFlags;
+        // If the onlyIfNeeded flag is set, then we can do this if the activity being launched
+        // is the same as the one making the call...  or, as a special case, if we do not know
+        // the caller then we count the current top activity as the caller.
+        if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+            ActivityRecord checkedCaller = sourceRecord;
+            if (checkedCaller == null) {
+                checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
+                        mNotTop);
+            }
+            if (!checkedCaller.realActivity.equals(r.realActivity)) {
+                // Caller is not the same as launcher, so always needed.
+                mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
+            }
+        }
+
+        mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
+    }
+
+    private void sendNewTaskResultRequestIfNeeded() {
+        if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0
+                && mStartActivity.resultTo.task.stack != null) {
+            // For whatever reason this activity is being launched into a new task...
+            // yet the caller has requested a result back.  Well, that is pretty messed up,
+            // so instead immediately send back a cancel and let the new task continue launched
+            // as normal without a dependency on its originator.
+            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+            mStartActivity.resultTo.task.stack.sendActivityResultLocked(-1, mStartActivity.resultTo,
+                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED, null);
+            mStartActivity.resultTo = null;
+        }
+    }
+
+    private void computeLaunchingTaskFlags() {
+        // If the caller is not coming from another activity, but has given us an explicit task into
+        // which they would like us to launch the new activity, then let's see about doing that.
+        if (mSourceRecord == null && mInTask != null && mInTask.stack != null) {
+            final Intent baseIntent = mInTask.getBaseIntent();
+            final ActivityRecord root = mInTask.getRootActivity();
+            if (baseIntent == null) {
+                ActivityOptions.abort(mOptions);
+                throw new IllegalArgumentException("Launching into task without base intent: "
+                        + mInTask);
+            }
+
+            // If this task is empty, then we are adding the first activity -- it
+            // determines the root, and must be launching as a NEW_TASK.
+            if (mLaunchSingleInstance || mLaunchSingleTask) {
+                if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
+                    ActivityOptions.abort(mOptions);
+                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
+                            + mStartActivity + " into different task " + mInTask);
+                }
+                if (root != null) {
+                    ActivityOptions.abort(mOptions);
+                    throw new IllegalArgumentException("Caller with mInTask " + mInTask
+                            + " has root " + root + " but target is singleInstance/Task");
+                }
+            }
+
+            // If task is empty, then adopt the interesting intent launch flags in to the
+            // activity being started.
+            if (root == null) {
+                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
+                        | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+                mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
+                        | (baseIntent.getFlags() & flagsOfInterest);
+                mIntent.setFlags(mLaunchFlags);
+                mInTask.setIntent(mStartActivity);
+                mAddingToTask = true;
+
+                // If the task is not empty and the caller is asking to start it as the root of
+                // a new task, then we don't actually want to start this on the task. We will
+                // bring the task to the front, and possibly give it a new intent.
+            } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+                mAddingToTask = false;
+
+            } else {
+                mAddingToTask = true;
+            }
+
+            mReuseTask = mInTask;
+        } else {
+            mInTask = null;
+            // Launch ResolverActivity in the source task, so that it stays in the task bounds
+            // when in freeform workspace.
+            // Also put noDisplay activities in the source task. These by itself can be placed
+            // in any task/stack, however it could launch other activities like ResolverActivity,
+            // and we want those to stay in the original task.
+            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
+                    && mSourceRecord.isFreeform())  {
+                mAddingToTask = true;
+            }
+        }
+
+        if (mInTask == null) {
+            if (mSourceRecord == null) {
+                // This activity is not being started from another...  in this
+                // case we -always- start a new task.
+                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
+                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
+                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
+                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+                }
+            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
+                // The original activity who is starting us is running as a single
+                // instance...  this new activity it is starting must go on its
+                // own task.
+                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            } else if (mLaunchSingleInstance || mLaunchSingleTask) {
+                // The activity being started is a single instance...  it always
+                // gets launched into its own task.
+                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            }
+        }
+    }
+
+    private void computeSourceStack() {
+        if (mSourceRecord == null) {
+            mSourceStack = null;
+            return;
+        }
+        if (!mSourceRecord.finishing) {
+            mSourceStack = mSourceRecord.task.stack;
+            return;
+        }
+
+        // If the source is finishing, we can't further count it as our source. This is because the
+        // task it is associated with may now be empty and on its way out, so we don't want to
+        // blindly throw it in to that task.  Instead we will take the NEW_TASK flow and try to find
+        // a task for it. But save the task information so it can be used when creating the new task.
+        if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
+            Slog.w(TAG, "startActivity called from finishing " + mSourceRecord
+                    + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
+            mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
+            mNewTaskInfo = mSourceRecord.info;
+            mNewTaskIntent = mSourceRecord.task.intent;
+        }
+        mSourceRecord = null;
+        mSourceStack = null;
+    }
+
+    /**
+     * Decide whether the new activity should be inserted into an existing task. Returns null
+     * if not or an ActivityRecord with the task into which the new activity should be added.
+     */
+    private ActivityRecord getReusableIntentActivity() {
+        // We may want to try to place the new activity in to an existing task.  We always
+        // do this if the target activity is singleTask or singleInstance; we will also do
+        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
+        // us to still place it in a new task: multi task, always doc mode, or being asked to
+        // launch this as a new task behind the current one.
+        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
+                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+                || mLaunchSingleInstance || mLaunchSingleTask;
+        // If bring to front is requested, and no result is requested and we have not been given
+        // an explicit task to launch in to, and we can find a task that was started with this
+        // same component, then instead of launching bring that one to the front.
+        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+        ActivityRecord intentActivity = null;
+        if (putIntoExistingTask) {
+            // See if there is a task to bring to the front.  If this is a SINGLE_INSTANCE
+            // activity, there can be one and only one instance of it in the history, and it is
+            // always in its own unique task, so we do a special search.
+            intentActivity = mLaunchSingleInstance ? mSupervisor.findActivityLocked(mIntent, mStartActivity.info)
+                    : mSupervisor.findTaskLocked(mStartActivity);
+        }
+        return intentActivity;
+    }
+
+    private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
+        mTargetStack = intentActivity.task.stack;
+        mTargetStack.mLastPausedActivity = null;
+        // If the target task is not in the front, then we need to bring it to the front...
+        // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
+        // the same behavior as if a new instance was being started, which means not bringing it
+        // to the front if the caller is not itself in the front.
+        final ActivityStack focusStack = mSupervisor.getFocusedStack();
+        ActivityRecord curTop = (focusStack == null)
+                ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
+
+        if (curTop != null && (curTop.task != intentActivity.task ||
+                curTop.task != focusStack.topTask())) {
+            mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+            if (mSourceRecord == null || (mSourceStack.topActivity() != null &&
+                    mSourceStack.topActivity().task == mSourceRecord.task)) {
+                // We really do want to push this one into the user's face, right now.
+                if (mLaunchTaskBehind && mSourceRecord != null) {
+                    intentActivity.setTaskToAffiliateWith(mSourceRecord.task);
+                }
+                mMovedHome = true;
+                final ActivityStack launchStack =
+                        getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task,
+                                mOptions, true);
+                if (launchStack == null || launchStack == mTargetStack) {
+                    // We only want to move to the front, if we aren't going to launch on a
+                    // different stack. If we launch on a different stack, we will put the
+                    // task on top there.
+                    mTargetStack.moveTaskToFrontLocked(intentActivity.task, mNoAnimation,
+                            mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
+                    mMovedToFront = true;
+                }
+                if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
+                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
+                    // Caller wants to appear on home activity.
+                    intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+                }
+                mOptions = null;
+            }
+        }
+        if (!mMovedToFront && mDoResume) {
+            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+                    + " from " + intentActivity);
+            mTargetStack.moveToFront("intentActivityFound");
+        }
+
+        // If the caller has requested that the target task be reset, then do so.
+        if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+            return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
+        }
+        return intentActivity;
+    }
+
+    private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
+        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
+            // The caller has requested to completely replace any existing task with its new
+            // activity. Well that should not be too hard...
+            mReuseTask = intentActivity.task;
+            mReuseTask.performClearTaskLocked();
+            mReuseTask.setIntent(mStartActivity);
+        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+                || mLaunchSingleInstance || mLaunchSingleTask) {
+            ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
+                    mLaunchFlags);
+            if (top == null) {
+                // A special case: we need to start the activity because it is not currently
+                // running, and the caller has asked to clear the current task to have this
+                // activity at the top.
+                mAddingToTask = true;
+                // Now pretend like this activity is being started by the top of its task, so it
+                // is put in the right place.
+                mSourceRecord = intentActivity;
+                final TaskRecord task = mSourceRecord.task;
+                if (task != null && task.stack == null) {
+                    // Target stack got cleared when we all activities were removed above.
+                    // Go ahead and reset it.
+                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
+                            null /* bounds */, mLaunchFlags, mOptions);
+                    mTargetStack.addTask(task,
+                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
+                }
+            }
+        } else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) {
+            // In this case the top activity on the task is the same as the one being launched,
+            // so we take that as a request to bring the task to the foreground. If the top
+            // activity in the task is the root activity, deliver this new intent to it if it
+            // desires.
+            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
+                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
+                        intentActivity.task);
+                if (intentActivity.frontOfTask) {
+                    intentActivity.task.setIntent(mStartActivity);
+                }
+                intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+                        mStartActivity.launchedFromPackage);
+            } else if (!mStartActivity.intent.filterEquals(intentActivity.task.intent)) {
+                // In this case we are launching the root activity of the task, but with a
+                // different intent. We should start a new instance on top.
+                mAddingToTask = true;
+                mSourceRecord = intentActivity;
+            }
+        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+            // In this case an activity is being launched in to an existing task, without
+            // resetting that task. This is typically the situation of launching an activity
+            // from a notification or shortcut. We want to place the new activity on top of the
+            // current task.
+            mAddingToTask = true;
+            mSourceRecord = intentActivity;
+        } else if (!intentActivity.task.rootWasReset) {
+            // In this case we are launching into an existing task that has not yet been started
+            // from its front door. The current task has been brought to the front. Ideally,
+            // we'd probably like to place this new task at the bottom of its stack, but that's
+            // a little hard to do with the current organization of the code so for now we'll
+            // just drop it.
+            intentActivity.task.setIntent(mStartActivity);
+        }
+    }
+
+    private void resumeTargetStackIfNeeded() {
+        if (mDoResume) {
+            mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, null, mOptions);
+            if (!mMovedToFront) {
+                // Make sure to notify Keyguard as well if we are not running an app transition
+                // later.
+                mSupervisor.notifyActivityDrawnForKeyguard();
+            }
+        } else {
+            ActivityOptions.abort(mOptions);
+        }
+        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+    }
+
+    private void setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
+        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchBounds, mLaunchFlags,
+                mOptions);
+        if (mDoResume) {
+            mTargetStack.moveToFront("startingNewTask");
+        }
+
+        if (mReuseTask == null) {
+            final TaskRecord task = mTargetStack.createTaskRecord(
+                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
+                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
+                    mNewTaskIntent != null ? mNewTaskIntent : mIntent,
+                    mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
+            mStartActivity.setTask(task, taskToAffiliate);
+            if (mLaunchBounds != null) {
+                mStartActivity.task.updateOverrideConfiguration(mLaunchBounds);
+            }
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                    "Starting new activity " +
+                            mStartActivity + " in new task " + mStartActivity.task);
+        } else {
+            mStartActivity.setTask(mReuseTask, taskToAffiliate);
+        }
+    }
+
+    private int setTaskFromSourceRecord() {
+        final TaskRecord sourceTask = mSourceRecord.task;
+        // We only want to allow changing stack if the target task is not the top one,
+        // otherwise we would move the launching task to the other side, rather than show
+        // two side by side.
+        final boolean launchToSideAllowed = sourceTask.stack.topTask() != sourceTask;
+        mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task, mOptions, launchToSideAllowed);
+
+        if (mTargetStack == null) {
+            mTargetStack = sourceTask.stack;
+        } else if (mTargetStack != sourceTask.stack) {
+            mSupervisor.moveTaskToStackLocked(sourceTask.taskId, mTargetStack.mStackId,
+                    ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE);
+        }
+        if (mDoResume) {
+            mTargetStack.moveToFront("sourceStackToFront");
+        }
+        final TaskRecord topTask = mTargetStack.topTask();
+        if (topTask != sourceTask) {
+            mTargetStack.moveTaskToFrontLocked(sourceTask, mNoAnimation, mOptions,
+                    mStartActivity.appTimeTracker, "sourceTaskToFront");
+        }
+        if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+            // In this case, we are adding the activity to an existing task, but the caller has
+            // asked to clear that task if the activity is already running.
+            ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
+            mKeepCurTransition = true;
+            if (top != null) {
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
+                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+                // For paranoia, make sure we have correctly resumed the top activity.
+                mTargetStack.mLastPausedActivity = null;
+                if (mDoResume) {
+                    mSupervisor.resumeFocusedStackTopActivityLocked();
+                }
+                ActivityOptions.abort(mOptions);
+                return START_DELIVERED_TO_TOP;
+            }
+        } else if (!mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
+            // In this case, we are launching an activity in our own task that may already be
+            // running somewhere in the history, and we want to shuffle it to the front of the
+            // stack if so.
+            final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
+            if (top != null) {
+                final TaskRecord task = top.task;
+                task.moveActivityToFrontLocked(top);
+                top.updateOptionsLocked(mOptions);
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
+                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+                mTargetStack.mLastPausedActivity = null;
+                if (mDoResume) {
+                    mSupervisor.resumeFocusedStackTopActivityLocked();
+                }
+                return START_DELIVERED_TO_TOP;
+            }
+        }
+
+        // An existing activity is starting this new activity, so we want to keep the new one in
+        // the same task as the one that is starting it.
+        mStartActivity.setTask(sourceTask, null);
+        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+                + " in existing task " + mStartActivity.task + " from source " + mSourceRecord);
+        return START_SUCCESS;
+    }
+
+    private int setTaskFromInTask() {
+        if (mLaunchBounds != null) {
+            mInTask.updateOverrideConfiguration(mLaunchBounds);
+            int stackId = mInTask.getLaunchStackId();
+            if (stackId != mInTask.stack.mStackId) {
+                mSupervisor.moveTaskToStackUncheckedLocked(
+                        mInTask, stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
+            }
+        }
+        mTargetStack = mInTask.stack;
+        mTargetStack.moveTaskToFrontLocked(
+                mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront");
+
+        // Check whether we should actually launch the new activity in to the task,
+        // or just reuse the current activity on top.
+        ActivityRecord top = mInTask.getTopActivity();
+        if (top != null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId) {
+            if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
+                    || mLaunchSingleTop || mLaunchSingleTask) {
+                ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
+                if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
+                    // We don't need to start a new activity, and the client said not to do
+                    // anything if that is the case, so this is it!
+                    return START_RETURN_INTENT_TO_CALLER;
+                }
+                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+                return START_DELIVERED_TO_TOP;
+            }
+        }
+
+        if (!mAddingToTask) {
+            // We don't actually want to have this activity added to the task, so just
+            // stop here but still tell the caller that we consumed the intent.
+            ActivityOptions.abort(mOptions);
+            return START_TASK_TO_FRONT;
+        }
+
+        mStartActivity.setTask(mInTask, null);
+        if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                "Starting new activity " + mStartActivity + " in explicit task " + mStartActivity.task);
+
+        return START_SUCCESS;
+    }
+
+    private void setTaskToCurrentTopOrCreateNewTask() {
+        mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
+                mOptions);
+        if (mDoResume) {
+            mTargetStack.moveToFront("addingToTopTask");
+        }
+        final ActivityRecord prev = mTargetStack.topActivity();
+        final TaskRecord task = (prev != null) ? prev.task : mTargetStack.createTaskRecord(
+                        mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
+                        mStartActivity.info, mIntent, null, null, true);
+        mStartActivity.setTask(task, null);
+        mWindowManager.moveTaskToTop(mStartActivity.task.taskId);
+        if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                "Starting new activity " + mStartActivity + " in new guessed " + mStartActivity.task);
+    }
+
+    private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
+            boolean launchSingleTask, int launchFlags) {
+        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+                (launchSingleInstance || launchSingleTask)) {
+            // We have a conflict between the Intent and the Activity manifest, manifest wins.
+            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
+                    "\"singleInstance\" or \"singleTask\"");
+            launchFlags &=
+                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
+        } else {
+            switch (r.info.documentLaunchMode) {
+                case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+                    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+                    break;
+                case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
+                    launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
+                    break;
+            }
+        }
+        return launchFlags;
     }
 
     final void doPendingActivityLaunchesLocked(boolean doResume) {
@@ -1389,9 +1595,8 @@
             PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
 
             try {
-                startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null,
-                        pal.startFlags, doResume && mPendingActivityLaunches.isEmpty(),
-                        null, null);
+                startActivityUnchecked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                        doResume && mPendingActivityLaunches.isEmpty(), null, null);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
@@ -1400,13 +1605,13 @@
     }
 
     private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
-            int launchFlags) {
+            int launchFlags, ActivityOptions aOptions) {
         final TaskRecord task = r.task;
         if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
             return mSupervisor.mHomeStack;
         }
 
-        ActivityStack stack = getLaunchToSideStack(r, launchFlags, task);
+        ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions, true);
         if (stack != null) {
             return stack;
         }
@@ -1442,7 +1647,7 @@
         final boolean canUseFocusedStack =
                 focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
                         || focusedStackId == DOCKED_STACK_ID
-                        || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable);
+                        || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeable());
         if (canUseFocusedStack && (!newTask
                 || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -1471,10 +1676,19 @@
         return stack;
     }
 
-    private ActivityStack getLaunchToSideStack(ActivityRecord r, int launchFlags, TaskRecord task) {
-        if ((launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) {
+    private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
+            ActivityOptions aOptions, boolean launchToSideAllowed) {
+        final int launchStackId =
+                (aOptions != null) ? aOptions.getLaunchStackId() : INVALID_STACK_ID;
+
+        if (isValidLaunchStackId(launchStackId, r)) {
+            return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
+        }
+
+        if (!launchToSideAllowed || (launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) {
             return null;
         }
+
         // The parent activity doesn't want to launch the activity on top of itself, but
         // instead tries to put it onto other side in side-by-side mode.
         final ActivityStack parentStack = task != null ? task.stack
@@ -1489,7 +1703,7 @@
             // and if yes, we will launch into that stack. If not, we just put the new
             // activity into parent's stack, because we can't find a better place.
             final ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
-            if (stack != null && !stack.isStackVisibleLocked()) {
+            if (stack != null && stack.getStackVisibilityLocked() == STACK_INVISIBLE) {
                 // There is a docked stack, but it isn't visible, so we can't launch into that.
                 return null;
             } else {
@@ -1498,36 +1712,39 @@
         }
     }
 
-    /**
-     * Decide whether the new activity should be inserted into an existing task. Returns null if not
-     * or an ActivityRecord with the task into which the new activity should be added.
-     */
-    private ActivityRecord getReusableIntentActivity(ActivityRecord r, TaskRecord inTask,
-            Intent intent, boolean launchSingleInstance, boolean launchSingleTask,
-            int launchFlags) {
-        // We may want to try to place the new activity in to an existing task.  We always
-        // do this if the target activity is singleTask or singleInstance; we will also do
-        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
-        // us to still place it in a new task: multi task, always doc mode, or being asked to
-        // launch this as a new task behind the current one.
-        boolean putIntoExistingTask = ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
-                (launchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
-                || launchSingleInstance || launchSingleTask;
-        // If bring to front is requested, and no result is requested and we have not
-        // been given an explicit task to launch in to, and
-        // we can find a task that was started with this same
-        // component, then instead of launching bring that one to the front.
-        putIntoExistingTask &= inTask == null && r.resultTo == null;
-        ActivityRecord intentActivity = null;
-        if (putIntoExistingTask) {
-            // See if there is a task to bring to the front.  If this is
-            // a SINGLE_INSTANCE activity, there can be one and only one
-            // instance of it in the history, and it is always in its own
-            // unique task, so we do a special search.
-            intentActivity = launchSingleInstance ? mSupervisor.findActivityLocked(intent, r.info)
-                    : mSupervisor.findTaskLocked(r);
+    private boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
+        if (stackId == INVALID_STACK_ID || stackId == HOME_STACK_ID
+                || !StackId.isStaticStack(stackId)) {
+            return false;
         }
-        return intentActivity;
+
+        final boolean resizeable = r.isResizeable() || mService.mForceResizableActivities;
+
+        if (stackId != FULLSCREEN_WORKSPACE_STACK_ID && !resizeable) {
+            return false;
+        }
+
+        if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
+            return false;
+        }
+
+        final boolean supportsPip = mService.mSupportsPictureInPicture
+                && (r.supportsPictureInPicture() || mService.mForceResizableActivities);
+        if (stackId == PINNED_STACK_ID && !supportsPip) {
+            return false;
+        }
+        return true;
+    }
+
+    Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
+        Rect newBounds = null;
+        if (options != null && (r.isResizeable() || (inTask != null && inTask.mResizeable))) {
+            if (mSupervisor.canUseActivityOptionsLaunchBounds(
+                    options, options.getLaunchStackId())) {
+                newBounds = TaskRecord.validateBounds(options.getLaunchBounds());
+            }
+        }
+        return newBounds;
     }
 
     void setWindowManager(WindowManagerService wm) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f64b803..82862e8 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1114,7 +1114,7 @@
                 } else {
                     // Not an option, last argument must be a package name.
                     try {
-                        reqUid = mContext.getPackageManager().getPackageUid(arg,
+                        reqUid = mContext.getPackageManager().getPackageUidAsUser(arg,
                                 UserHandle.getCallingUserId());
                     } catch (PackageManager.NameNotFoundException e) {
                         pw.println("Unknown package: " + arg);
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 0397553..f2e8d09 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -100,6 +100,6 @@
 30045 am_pre_boot (User|1|5),(Package|3)
 
 # Report collection of global memory state
-30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2)
+30046 am_meminfo (Cached|2|2),(Free|2|2),(Zram|2|2),(Kernel|2|2),(Native|2|2)
 # Report collection of memory used by a process
-30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2)
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 4bfe300..b4aa4cf 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -82,7 +82,9 @@
     long lastStateTime;         // Last time setProcState changed
     long initialIdlePss;        // Initial memory pss of process for idle maintenance.
     long lastPss;               // Last computed memory pss.
+    long lastSwapPss;           // Last computed SwapPss.
     long lastCachedPss;         // Last computed pss when in cached state.
+    long lastCachedSwapPss;     // Last computed SwapPss when in cached state.
     int maxAdj;                 // Maximum OOM adjustment for this process
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
@@ -257,7 +259,9 @@
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPss="); DebugUtils.printSizeValue(pw, lastPss*1024);
+                pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss*1024);
                 pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss*1024);
+                pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, lastCachedSwapPss*1024);
                 pw.println();
         pw.print(prefix); pw.print("cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index c63eaac..a3c26cb 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -16,7 +16,12 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 import android.app.ActivityManager;
@@ -27,11 +32,16 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 
+import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -47,18 +57,106 @@
     // Maximum number recent bitmaps to keep in memory.
     private static final int MAX_RECENT_BITMAPS = 3;
 
-    // Activity manager service.
-    private final ActivityManagerService mService;
+    /**
+     * Save recent tasks information across reboots.
+     */
+    private final TaskPersister mTaskPersister;
+    private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(5);
 
     // Mainly to avoid object recreation on multiple calls.
     private final ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>();
-    private final HashMap<ComponentName, ActivityInfo> tmpAvailActCache = new HashMap<>();
-    private final HashMap<String, ApplicationInfo> tmpAvailAppCache = new HashMap<>();
-    private final ActivityInfo tmpActivityInfo = new ActivityInfo();
-    private final ApplicationInfo tmpAppInfo = new ApplicationInfo();
+    private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
+    private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
+    private final ActivityInfo mTmpActivityInfo = new ActivityInfo();
+    private final ApplicationInfo mTmpAppInfo = new ApplicationInfo();
 
-    RecentTasks(ActivityManagerService service) {
-        mService = service;
+    RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
+        File systemDir = Environment.getDataSystemDirectory();
+        mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this);
+        mStackSupervisor.setRecentTasks(this);
+    }
+
+    /**
+     * Loads the persistent recentTasks for {@code userId} into {@link #mRecentTasks} from
+     * persistent storage. Does nothing if they are already loaded.
+     *
+     * @param userId the user Id
+     */
+    void loadUserRecentsLocked(int userId) {
+        if (!mUsersWithRecentsLoaded.get(userId)) {
+            Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
+            addAll(mTaskPersister.restoreTasksForUserLocked(userId));
+            cleanupLocked(userId);
+            mUsersWithRecentsLoaded.put(userId, true);
+        }
+    }
+
+    void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
+        if (task != null && task.stack != null && task.stack.isHomeStack()) {
+            // Never persist the home stack.
+            return;
+        }
+        mTaskPersister.wakeup(task, flush);
+    }
+
+    void onSystemReady() {
+        clear();
+        loadUserRecentsLocked(UserHandle.USER_SYSTEM);
+        startPersisting();
+    }
+
+    void startPersisting() {
+        mTaskPersister.startPersisting();
+    }
+
+    Bitmap getTaskDescriptionIcon(String path) {
+        return mTaskPersister.getTaskDescriptionIcon(path);
+    }
+
+    Bitmap getImageFromWriteQueue(String path) {
+        return mTaskPersister.getImageFromWriteQueue(path);
+    }
+
+    void saveImage(Bitmap image, String path) {
+        mTaskPersister.saveImage(image, path);
+    }
+
+    void flush() {
+        mTaskPersister.flush();
+    }
+
+    /**
+     * Returns all userIds for which recents from storage are loaded
+     *
+     * @return an array of userIds.
+     */
+    int[] usersWithRecentsLoadedLocked() {
+        int[] usersWithRecentsLoaded = new int[mUsersWithRecentsLoaded.size()];
+        int len = 0;
+        for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
+            int userId = mUsersWithRecentsLoaded.keyAt(i);
+            if (mUsersWithRecentsLoaded.valueAt(i)) {
+                usersWithRecentsLoaded[len++] = userId;
+            }
+        }
+        if (len < usersWithRecentsLoaded.length) {
+            // should never happen.
+            return Arrays.copyOf(usersWithRecentsLoaded, len);
+        }
+        return usersWithRecentsLoaded;
+    }
+
+    /**
+     * Removes recent tasks for this user if they are loaded, does not do anything otherwise.
+     *
+     * @param userId the user id.
+     */
+    void unloadUserRecentsLocked(int userId) {
+        if (mUsersWithRecentsLoaded.get(userId)) {
+            Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
+            mUsersWithRecentsLoaded.delete(userId);
+            removeTasksForUserLocked(userId);
+        }
     }
 
     TaskRecord taskForIdLocked(int id) {
@@ -88,9 +186,6 @@
                 tr.removedFromRecents();
             }
         }
-
-        // Remove tasks from persistent storage.
-        mService.notifyTaskPersisterLocked(null, true);
     }
 
     /**
@@ -107,86 +202,86 @@
         }
 
         final IPackageManager pm = AppGlobals.getPackageManager();
-        final int[] users = (userId == UserHandle.USER_ALL)
-                ? mService.mUserController.getUsers() : new int[] { userId };
-        for (int userIdx = 0; userIdx < users.length; userIdx++) {
-            final int user = users[userIdx];
-            recentsCount = size() - 1;
-            for (int i = recentsCount; i >= 0; i--) {
-                TaskRecord task = get(i);
-                if (task.userId != user) {
-                    // Only look at tasks for the user ID of interest.
-                    continue;
-                }
-                if (task.autoRemoveRecents && task.getTopActivity() == null) {
-                    // This situation is broken, and we should just get rid of it now.
-                    remove(i);
-                    task.removedFromRecents();
-                    Slog.w(TAG, "Removing auto-remove without activity: " + task);
-                    continue;
-                }
-                // Check whether this activity is currently available.
-                if (task.realActivity != null) {
-                    ActivityInfo ai = tmpAvailActCache.get(task.realActivity);
+        for (int i = recentsCount - 1; i >= 0; i--) {
+            final TaskRecord task = get(i);
+            if (userId != UserHandle.USER_ALL && task.userId != userId) {
+                // Only look at tasks for the user ID of interest.
+                continue;
+            }
+            if (task.autoRemoveRecents && task.getTopActivity() == null) {
+                // This situation is broken, and we should just get rid of it now.
+                remove(i);
+                task.removedFromRecents();
+                Slog.w(TAG, "Removing auto-remove without activity: " + task);
+                continue;
+            }
+            // Check whether this activity is currently available.
+            if (task.realActivity != null) {
+                ActivityInfo ai = mTmpAvailActCache.get(task.realActivity);
+                if (ai == null) {
+                    try {
+                        // At this first cut, we're only interested in
+                        // activities that are fully runnable based on
+                        // current system state.
+                        ai = pm.getActivityInfo(task.realActivity,
+                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+                    } catch (RemoteException e) {
+                        // Will never happen.
+                        continue;
+                    }
                     if (ai == null) {
+                        ai = mTmpActivityInfo;
+                    }
+                    mTmpAvailActCache.put(task.realActivity, ai);
+                }
+                if (ai == mTmpActivityInfo) {
+                    // This could be either because the activity no longer exists, or the
+                    // app is temporarily gone. For the former we want to remove the recents
+                    // entry; for the latter we want to mark it as unavailable.
+                    ApplicationInfo app = mTmpAvailAppCache
+                            .get(task.realActivity.getPackageName());
+                    if (app == null) {
                         try {
-                            ai = pm.getActivityInfo(task.realActivity,
-                                    PackageManager.GET_UNINSTALLED_PACKAGES
-                                            | PackageManager.GET_DISABLED_COMPONENTS, user);
+                            app = pm.getApplicationInfo(task.realActivity.getPackageName(),
+                                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
                         } catch (RemoteException e) {
                             // Will never happen.
                             continue;
                         }
-                        if (ai == null) {
-                            ai = tmpActivityInfo;
-                        }
-                        tmpAvailActCache.put(task.realActivity, ai);
-                    }
-                    if (ai == tmpActivityInfo) {
-                        // This could be either because the activity no longer exists, or the
-                        // app is temporarily gone.  For the former we want to remove the recents
-                        // entry; for the latter we want to mark it as unavailable.
-                        ApplicationInfo app = tmpAvailAppCache.get(task.realActivity.getPackageName());
                         if (app == null) {
-                            try {
-                                app = pm.getApplicationInfo(task.realActivity.getPackageName(),
-                                        PackageManager.GET_UNINSTALLED_PACKAGES
-                                                | PackageManager.GET_DISABLED_COMPONENTS, user);
-                            } catch (RemoteException e) {
-                                // Will never happen.
-                                continue;
-                            }
-                            if (app == null) {
-                                app = tmpAppInfo;
-                            }
-                            tmpAvailAppCache.put(task.realActivity.getPackageName(), app);
+                            app = mTmpAppInfo;
                         }
-                        if (app == tmpAppInfo || (app.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
-                            // Doesn't exist any more!  Good-bye.
-                            remove(i);
-                            task.removedFromRecents();
-                            Slog.w(TAG, "Removing no longer valid recent: " + task);
-                            continue;
-                        } else {
-                            // Otherwise just not available for now.
-                            if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
-                                    "Making recent unavailable: " + task);
-                            task.isAvailable = false;
-                        }
+                        mTmpAvailAppCache.put(task.realActivity.getPackageName(), app);
+                    }
+                    if (app == mTmpAppInfo
+                            || (app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                        // Doesn't exist any more! Good-bye.
+                        remove(i);
+                        task.removedFromRecents();
+                        Slog.w(TAG, "Removing no longer valid recent: " + task);
+                        continue;
                     } else {
-                        if (!ai.enabled || !ai.applicationInfo.enabled
-                                || (ai.applicationInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
-                            if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
-                                    "Making recent unavailable: " + task
-                                    + " (enabled=" + ai.enabled + "/" + ai.applicationInfo.enabled
-                                    + " flags=" + Integer.toHexString(ai.applicationInfo.flags)
-                                    + ")");
-                            task.isAvailable = false;
-                        } else {
-                            if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
-                                    "Making recent available: " + task);
-                            task.isAvailable = true;
-                        }
+                        // Otherwise just not available for now.
+                        if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
+                                "Making recent unavailable: " + task);
+                        task.isAvailable = false;
+                    }
+                } else {
+                    if (!ai.enabled || !ai.applicationInfo.enabled
+                            || (ai.applicationInfo.flags
+                                    & ApplicationInfo.FLAG_INSTALLED) == 0) {
+                        if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
+                                "Making recent unavailable: " + task
+                                        + " (enabled=" + ai.enabled + "/"
+                                        + ai.applicationInfo.enabled
+                                        + " flags="
+                                        + Integer.toHexString(ai.applicationInfo.flags)
+                                        + ")");
+                        task.isAvailable = false;
+                    } else {
+                        if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
+                                "Making recent available: " + task);
+                        task.isAvailable = true;
                     }
                 }
             }
@@ -340,7 +435,7 @@
                     // Simple case: this is not an affiliated task, so we just move it to the front.
                     remove(taskIndex);
                     add(0, task);
-                    mService.notifyTaskPersisterLocked(task, false);
+                    notifyTaskPersisterLocked(task, false);
                     if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
                             + " from " + taskIndex);
                     return;
@@ -500,7 +595,7 @@
                 // specified, then replace it with the existing recent task.
                 task = tr;
             }
-            mService.notifyTaskPersisterLocked(tr, false);
+            notifyTaskPersisterLocked(tr, false);
         }
 
         return -1;
@@ -550,7 +645,7 @@
         if (first.mNextAffiliate != null) {
             Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
             first.setNextAffiliate(null);
-            mService.notifyTaskPersisterLocked(first, false);
+            notifyTaskPersisterLocked(first, false);
         }
         // Everything in the middle is doubly linked from next to prev.
         final int tmpSize = mTmpRecents.size();
@@ -561,13 +656,13 @@
                 Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
                         " setting prev=" + prev);
                 next.setPrevAffiliate(prev);
-                mService.notifyTaskPersisterLocked(next, false);
+                notifyTaskPersisterLocked(next, false);
             }
             if (prev.mNextAffiliate != next) {
                 Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
                         " setting next=" + next);
                 prev.setNextAffiliate(next);
-                mService.notifyTaskPersisterLocked(prev, false);
+                notifyTaskPersisterLocked(prev, false);
             }
             prev.inRecents = true;
         }
@@ -576,7 +671,7 @@
         if (last.mPrevAffiliate != null) {
             Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
             last.setPrevAffiliate(null);
-            mService.notifyTaskPersisterLocked(last, false);
+            notifyTaskPersisterLocked(last, false);
         }
 
         // Insert the group back into mRecentTasks at start.
diff --git a/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java
new file mode 100644
index 0000000..ff39589
--- /dev/null
+++ b/services/core/java/com/android/server/am/ResizeDockedStackTimeout.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.graphics.Rect;
+import android.os.Handler;
+
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+
+/**
+ * When resizing the docked stack, a caller can temporarily supply task bounds that are different
+ * from the stack bounds. In order to return to a sane state if the caller crashes or has a bug,
+ * this class manages this cycle.
+ */
+class ResizeDockedStackTimeout {
+
+    private static final long TIMEOUT_MS = 10 * 1000;
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mSupervisor;
+    private final Handler mHandler;
+    private final Rect mCurrentDockedBounds = new Rect();
+
+    private final Runnable mTimeoutRunnable = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mService) {
+                mSupervisor.resizeDockedStackLocked(mCurrentDockedBounds, null, null, null, null,
+                        PRESERVE_WINDOWS);
+            }
+        }
+    };
+
+    ResizeDockedStackTimeout(ActivityManagerService service, ActivityStackSupervisor supervisor,
+            Handler handler) {
+        mService = service;
+        mSupervisor = supervisor;
+        mHandler = handler;
+    }
+
+    void notifyResizing(Rect dockedBounds, boolean hasTempBounds) {
+        mHandler.removeCallbacks(mTimeoutRunnable);
+        if (!hasTempBounds) {
+            return;
+        }
+        mCurrentDockedBounds.set(dockedBounds);
+        mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 9a00075..283939e 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -21,16 +21,18 @@
 import android.os.Debug;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Process;
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
-import android.os.Process;
 
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -42,12 +44,10 @@
 import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import libcore.io.IoUtils;
-
 public class TaskPersister {
     static final String TAG = "TaskPersister";
     static final boolean DEBUG = false;
@@ -113,7 +113,7 @@
     ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
 
     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
-            RecentTasks recentTasks) {
+            ActivityManagerService service, RecentTasks recentTasks) {
 
         final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
         if (legacyImagesDir.exists()) {
@@ -130,7 +130,7 @@
         }
 
         mStackSupervisor = stackSupervisor;
-        mService = stackSupervisor.mService;
+        mService = service;
         mRecentTasks = recentTasks;
         mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
     }
@@ -145,11 +145,15 @@
         final String taskString = Integer.toString(task.taskId);
         for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
             final WriteQueueItem item = mWriteQueue.get(queueNdx);
-            if (item instanceof ImageWriteQueueItem &&
-                    ((ImageWriteQueueItem) item).mFilePath.startsWith(taskString)) {
-                if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
-                        " from write queue");
-                mWriteQueue.remove(queueNdx);
+            if (item instanceof ImageWriteQueueItem) {
+                final File thumbnailFile = new File(((ImageWriteQueueItem) item).mFilePath);
+                if (thumbnailFile.getName().startsWith(taskString)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
+                                " from write queue");
+                    }
+                    mWriteQueue.remove(queueNdx);
+                }
             }
         }
     }
@@ -185,7 +189,8 @@
                     mWriteQueue.add(new TaskWriteQueueItem(task));
                 }
             } else {
-                // Dummy.
+                // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
+                // notified.
                 mWriteQueue.add(new WriteQueueItem());
             }
             if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
@@ -323,8 +328,8 @@
         return null;
     }
 
-    private List<TaskRecord> restoreTasksForUserLocked(final int userId) {
-        final List<TaskRecord> tasks = new ArrayList<TaskRecord>();
+    List<TaskRecord> restoreTasksForUserLocked(final int userId) {
+        final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
         ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
 
         File userTasksDir = getUserTasksDir(userId);
@@ -351,10 +356,10 @@
                         event != XmlPullParser.END_TAG) {
                     final String name = in.getName();
                     if (event == XmlPullParser.START_TAG) {
-                        if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
+                        if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name);
                         if (TAG_TASK.equals(name)) {
                             final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
-                            if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task="
+                            if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task="
                                     + task);
                             if (task != null) {
                                 // XXX Don't add to write queue... there is no reason to write
@@ -362,7 +367,7 @@
                                 // read the same thing again.
                                 // mWriteQueue.add(new TaskWriteQueueItem(task));
                                 final int taskId = task.taskId;
-                                mStackSupervisor.setNextTaskId(taskId);
+                                mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
                                 // Check if it's a valid user id. Don't add tasks for removed users.
                                 if (userId == task.userId) {
                                     task.isPersistable = true;
@@ -396,15 +401,6 @@
         if (!DEBUG) {
             removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles());
         }
-        return tasks;
-    }
-
-    ArrayList<TaskRecord> restoreTasksLocked(final int[] validUserIds) {
-        final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
-
-        for (int userId : validUserIds) {
-            tasks.addAll(restoreTasksForUserLocked(userId));
-        }
 
         // Fix up task affiliation from taskIds
         for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -413,9 +409,7 @@
             task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks));
         }
 
-        TaskRecord[] tasksArray = new TaskRecord[tasks.size()];
-        tasks.toArray(tasksArray);
-        Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
+        Collections.sort(tasks, new Comparator<TaskRecord>() {
             @Override
             public int compare(TaskRecord lhs, TaskRecord rhs) {
                 final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
@@ -428,8 +422,7 @@
                 }
             }
         });
-
-        return new ArrayList<TaskRecord>(Arrays.asList(tasksArray));
+        return tasks;
     }
 
     private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
@@ -462,7 +455,13 @@
     }
 
     private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
-        for (int userId : mService.getRunningUserIds()) {
+        int[] candidateUserIds;
+        synchronized (mService) {
+            // Remove only from directories of the users who have recents in memory synchronized
+            // with persistent storage.
+            candidateUserIds = mRecentTasks.usersWithRecentsLoadedLocked();
+        }
+        for (int userId : candidateUserIds) {
             removeObsoleteFiles(persistentTaskIds, getUserImagesDir(userId).listFiles());
             removeObsoleteFiles(persistentTaskIds, getUserTasksDir(userId).listFiles());
         }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4647d77..be36f01 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
@@ -23,6 +24,7 @@
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
@@ -441,7 +443,7 @@
         } else {
             autoRemoveRecents = false;
         }
-        mResizeable = info.resizeable || mService.mForceResizableActivities;
+        mResizeable = (info.flags & FLAG_RESIZEABLE) != 0 || mService.mForceResizableActivities;
         mLockTaskMode = info.lockTaskLaunchMode;
         mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
         setLockTaskAuth();
@@ -550,7 +552,7 @@
                     mLastThumbnailFile.delete();
                 }
             } else {
-                mService.mTaskPersister.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath());
+                mService.mRecentTasks.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath());
             }
             return true;
         }
@@ -562,7 +564,7 @@
         thumbs.thumbnailInfo = mLastThumbnailInfo;
         thumbs.thumbnailFileDescriptor = null;
         if (mLastThumbnail == null) {
-            thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(
+            thumbs.mainThumbnail = mService.mRecentTasks.getImageFromWriteQueue(
                     mLastThumbnailFile.getAbsolutePath());
         }
         // Only load the thumbnail file if we don't have a thumbnail
@@ -697,7 +699,7 @@
         if (mActivities.isEmpty()) {
             taskType = r.mActivityType;
             if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivities) {
-                mResizeable = r.info.resizeable;
+                mResizeable = r.isResizeable();
             }
             isPersistable = r.isPersistable();
             mCallingUid = r.launchedFromUid;
@@ -1297,6 +1299,9 @@
                     (mOverrideConfig.screenWidthDp <= mOverrideConfig.screenHeightDp)
                             ? Configuration.ORIENTATION_PORTRAIT
                             : Configuration.ORIENTATION_LANDSCAPE;
+            final int sl = Configuration.resetScreenLayout(serviceConfig.screenLayout);
+            mOverrideConfig.screenLayout = Configuration.reduceScreenLayout(
+                    sl, mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
         }
 
         if (mFullscreen != oldFullscreen) {
@@ -1306,6 +1311,20 @@
         return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
     }
 
+    Rect updateOverrideConfigurationFromLaunchBounds() {
+        final Rect bounds = validateBounds(getLaunchBounds());
+        updateOverrideConfiguration(bounds);
+        return bounds;
+    }
+
+    static Rect validateBounds(Rect bounds) {
+        if (bounds != null && bounds.isEmpty()) {
+            Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable());
+            return null;
+        }
+        return bounds;
+    }
+
     private void reportMultiWindowModeChange() {
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = mActivities.get(i);
@@ -1321,7 +1340,12 @@
         }
     }
 
-    void reportPictureInPictureModeChange() {
+    void reportPictureInPictureModeChangeIfNeeded(ActivityStack prevStack) {
+        if (prevStack == null || prevStack == stack
+                || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
+            return;
+        }
+
         for (int i = mActivities.size() - 1; i >= 0; i--) {
             final ActivityRecord r = mActivities.get(i);
             if (r.app != null && r.app.thread != null) {
@@ -1377,17 +1401,20 @@
 
     /** Returns the bounds that should be used to launch this task. */
     Rect getLaunchBounds() {
-        final int stackId = stack.mStackId;
-
         // If we're over lockscreen, forget about stack bounds and use fullscreen.
         if (mService.mLockScreenShown == LOCK_SCREEN_SHOWN) {
             return null;
         }
 
-        if (stack == null
-                || stackId == HOME_STACK_ID
-                || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
-            return (mResizeable && stack != null) ? stack.mBounds : null;
+        if (stack == null) {
+            return null;
+        }
+
+        final int stackId = stack.mStackId;
+        if (stackId == HOME_STACK_ID
+                || stackId == FULLSCREEN_WORKSPACE_STACK_ID
+                || (stackId == DOCKED_STACK_ID && !mResizeable)) {
+            return mResizeable ? stack.mBounds : null;
         } else if (!StackId.persistTaskBounds(stackId)) {
             return stack.mBounds;
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 20b1e60..8c16872 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -22,6 +22,7 @@
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.content.Context.KEYGUARD_SERVICE;
 import static android.os.Process.SYSTEM_UID;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -43,6 +44,7 @@
 import android.app.Dialog;
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
+import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -73,6 +75,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.server.pm.UserManagerService;
 
 import java.io.PrintWriter;
@@ -140,6 +143,8 @@
 
     private volatile UserManagerService mUserManager;
 
+    private final LockPatternUtils mLockPatternUtils;
+
     UserController(ActivityManagerService service) {
         mService = service;
         mHandler = mService.mHandler;
@@ -147,6 +152,7 @@
         final UserState uss = new UserState(UserHandle.SYSTEM);
         mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
         mUserLru.add(UserHandle.USER_SYSTEM);
+        mLockPatternUtils = new LockPatternUtils(mService.mContext);
         updateStartedUserArrayLocked();
     }
 
@@ -243,6 +249,9 @@
             if (uss.state == UserState.STATE_RUNNING_LOCKED) {
                 uss.setState(UserState.STATE_RUNNING);
 
+                // Give user manager a chance to prepare app storage
+                mUserManager.onBeforeUnlockUser(userId);
+
                 mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
 
                 final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
@@ -304,7 +313,10 @@
                         + relatedUserId);
                 // We still need to stop the requested user if it's a force stop.
                 if (force) {
+                    Slog.i(TAG,
+                            "Force stop user " + userId + ". Related users will not be stopped");
                     stopSingleUserLocked(userId, callback);
+                    return USER_OP_SUCCESS;
                 }
                 return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
             }
@@ -413,6 +425,7 @@
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLocked();
 
+                mService.onUserStoppedLocked(userId);
                 // Clean up all state and processes associated with the user.
                 // Kill all the processes for the user.
                 forceStopUserLocked(userId, "finish user");
@@ -642,7 +655,8 @@
                 }
 
                 if (uss.state == UserState.STATE_BOOTING) {
-                    // Let user manager propagate user restrictions to other services.
+                    // Give user manager a chance to propagate user restrictions
+                    // to other services and prepare app storage
                     getUserManager().onBeforeStartUser(userId);
 
                     // Booting up a new user, need to tell system services about it.
@@ -931,7 +945,7 @@
         if (homeInFront) {
             mService.startHomeActivityLocked(newUserId, "moveUserToForeground");
         } else {
-            mService.mStackSupervisor.resumeTopActivitiesLocked();
+            mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
         }
         EventLogTags.writeAmSwitchUser(newUserId);
         getUserManager().onUserForeground(newUserId);
@@ -1115,8 +1129,7 @@
             UserState uss = mStartedUsers.valueAt(i);
             if (uss.state != UserState.STATE_STOPPING
                     && uss.state != UserState.STATE_SHUTDOWN) {
-                mStartedUserArray[num] = mStartedUsers.keyAt(i);
-                num++;
+                mStartedUserArray[num++] = mStartedUsers.keyAt(i);
             }
         }
     }
@@ -1283,6 +1296,19 @@
         return mCurrentProfileIds;
     }
 
+    /**
+     * Returns whether the given user requires credential entry at this time. This is used to
+     * intercept activity launches for work apps when the Work Challenge is present.
+     */
+    boolean shouldConfirmCredentials(int userId) {
+        if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            return false;
+        }
+        final KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(KEYGUARD_SERVICE);
+        return km.isDeviceLocked(userId);
+    }
+
     void dump(PrintWriter pw, boolean dumpAll) {
         pw.println("  mStartedUsers:");
         for (int i = 0; i < mStartedUsers.size(); i++) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 40d01e7..9331dd8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -58,10 +58,12 @@
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioPort;
+import android.media.AudioRecordConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioService;
+import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
 import android.media.IVolumeController;
 import android.media.MediaPlayer;
@@ -220,6 +222,7 @@
     private static final int MSG_UNMUTE_STREAM = 24;
     private static final int MSG_DYN_POLICY_MIX_STATE_UPDATE = 25;
     private static final int MSG_INDICATE_SYSTEM_READY = 26;
+    private static final int MSG_PERSIST_MASTER_MONO = 27;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -705,6 +708,8 @@
         LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
 
         mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+
+        mRecordMonitor.initMonitor();
     }
 
     public void systemReady() {
@@ -820,6 +825,12 @@
             streamState.applyAllVolumes();
         }
 
+        // Restore mono mode
+        final boolean masterMono = System.getIntForUser(
+                mContentResolver, System.MASTER_MONO,
+                0 /* default */, UserHandle.USER_CURRENT) == 1;
+        AudioSystem.setMasterMono(masterMono);
+
         // Restore ringer mode
         setRingerModeInt(getRingerModeInternal(), false);
 
@@ -1079,6 +1090,14 @@
         }
         AudioSystem.muteMicrophone(microphoneMute);
 
+        final boolean masterMono = System.getIntForUser(
+                cr, System.MASTER_MONO, 0 /* default */, UserHandle.USER_CURRENT) == 1;
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Master mono %b, user=%d", masterMono, currentUser));
+        }
+        AudioSystem.setMasterMono(masterMono);
+        broadcastMasterMonoStatus(masterMono);
+
         // Each stream will read its own persisted settings
 
         // Broadcast the sticky intents
@@ -1835,6 +1854,52 @@
                 userId);
     }
 
+    /** @hide */
+    public boolean isMasterMono() {
+        return AudioSystem.getMasterMono();
+    }
+
+    /** @hide */
+    public void setMasterMono(boolean mono, String callingPackage, int userId) {
+        int callingUid = Binder.getCallingUid();
+        // If we are being called by the system check for user we are going to change
+        // so we handle user restrictions correctly.
+        if (callingUid == android.os.Process.SYSTEM_UID) {
+            callingUid = UserHandle.getUid(userId, UserHandle.getAppId(callingUid));
+        }
+
+        if (userId != UserHandle.getCallingUserId() &&
+                mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        if (DEBUG_VOL) {
+            Log.d(TAG, String.format("Master mono %b, user=%d", mono, userId));
+        }
+
+        if (getCurrentUserId() == userId) {
+            if (mono != AudioSystem.getMasterMono()) {
+                AudioSystem.setMasterMono(mono);
+                // Post a persist master mono msg
+                sendMsg(mAudioHandler, MSG_PERSIST_MASTER_MONO, SENDMSG_REPLACE, mono ? 1
+                        : 0 /* value */, userId, null /* obj */, 0 /* delay */);
+                // notify apps and settings
+                broadcastMasterMonoStatus(mono);
+            }
+        } else {
+            // Post a persist master mono msg
+            sendMsg(mAudioHandler, MSG_PERSIST_MASTER_MONO, SENDMSG_REPLACE, mono ? 1
+                    : 0 /* value */, userId, null /* obj */, 0 /* delay */);
+        }
+    }
+
+    private void broadcastMasterMonoStatus(boolean mono) {
+        Intent intent = new Intent(AudioManager.MASTER_MONO_CHANGED_ACTION);
+        intent.putExtra(AudioManager.EXTRA_MASTER_MONO, mono);
+        sendBroadcastToAll(intent);
+    }
+
     /** @see AudioManager#getStreamVolume(int) */
     public int getStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
@@ -4534,6 +4599,13 @@
                 case MSG_DYN_POLICY_MIX_STATE_UPDATE:
                     onDynPolicyMixStateUpdate((String) msg.obj, msg.arg1);
                     break;
+
+                case MSG_PERSIST_MASTER_MONO:
+                    Settings.System.putIntForUser(mContentResolver,
+                                                 Settings.System.MASTER_MONO,
+                                                 msg.arg1 /* value */,
+                                                 msg.arg2 /* userHandle */);
+                    break;
             }
         }
     }
@@ -6097,7 +6169,7 @@
     }
 
     //======================
-    // Audio policy callback from AudioSystem
+    // Audio policy callbacks from AudioSystem for dynamic policies
     //======================
     private final AudioSystem.DynamicPolicyCallback mDynPolicyCallback =
             new AudioSystem.DynamicPolicyCallback() {
@@ -6126,7 +6198,23 @@
                 }
             }
         }
+    }
 
+    //======================
+    // Audio policy callbacks from AudioSystem for recording configuration updates
+    //======================
+    private final RecordingActivityMonitor mRecordMonitor = new RecordingActivityMonitor();
+
+    public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        mRecordMonitor.registerRecordingCallback(rcdb);
+    }
+
+    public void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        mRecordMonitor.unregisterRecordingCallback(rcdb);
+    }
+
+    public AudioRecordConfiguration[] getActiveRecordConfigurations() {
+        return mRecordMonitor.getActiveRecordConfigurations();
     }
 
     //======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
new file mode 100644
index 0000000..5806f3f
--- /dev/null
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -0,0 +1,169 @@
+/*
+ * 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.server.audio;
+
+import android.media.AudioManager;
+import android.media.AudioRecordConfiguration;
+import android.media.AudioSystem;
+import android.media.IRecordingConfigDispatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Class to receive and dispatch updates from AudioSystem about recording configurations.
+ */
+public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback {
+
+    public final static String TAG = "AudioService.RecordingActivityMonitor";
+
+    private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
+
+    private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs =
+            new HashMap<Integer, AudioRecordConfiguration>();
+
+    RecordingActivityMonitor() {
+        RecMonitorClient.sMonitor = this;
+    }
+
+    /**
+     * Implementation of android.media.AudioSystem.AudioRecordingCallback
+     */
+    public void onRecordingConfigurationChanged(int event, int session, int source) {
+        if (updateSnapshot(event, session, source)) {
+            final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+            synchronized(mClients) {
+                while (clientIterator.hasNext()) {
+                    try {
+                        clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
+                    }
+                }
+            }
+        }
+    }
+
+    void initMonitor() {
+        AudioSystem.setRecordingCallback(this);
+    }
+
+    void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        if (rcdb == null) {
+            return;
+        }
+        synchronized(mClients) {
+            final RecMonitorClient rmc = new RecMonitorClient(rcdb);
+            if (rmc.init()) {
+                mClients.add(rmc);
+            }
+        }
+    }
+
+    void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
+        if (rcdb == null) {
+            return;
+        }
+        synchronized(mClients) {
+            final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+            while (clientIterator.hasNext()) {
+                RecMonitorClient rmc = clientIterator.next();
+                if (rcdb.equals(rmc.mDispatcherCb)) {
+                    rmc.release();
+                    clientIterator.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+    AudioRecordConfiguration[] getActiveRecordConfigurations() {
+        synchronized(mRecordConfigs) {
+            return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+        }
+    }
+
+    /**
+     * Update the internal "view" of the active recording sessions
+     * @param event
+     * @param session
+     * @param source
+     * @return true if the list of active recording sessions has been modified, false otherwise.
+     */
+    private boolean updateSnapshot(int event, int session, int source) {
+        synchronized(mRecordConfigs) {
+            switch (event) {
+            case AudioManager.RECORD_CONFIG_EVENT_STOP:
+                // return failure if an unknown recording session stopped
+                return (mRecordConfigs.remove(new Integer(session)) != null);
+            case AudioManager.RECORD_CONFIG_EVENT_START:
+                if (mRecordConfigs.containsKey(new Integer(session))) {
+                    // start of session that's already tracked, not worth an update
+                    // TO DO in the future when tracking record format: there might be a record
+                    //       format change during a recording that requires reporting
+                    return false;
+                } else {
+                    mRecordConfigs.put(new Integer(session),
+                            new AudioRecordConfiguration(session, source));
+                    return true;
+                }
+            default:
+                Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
+                        event, session, source));
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Inner class to track clients that want to be notified of recording updates
+     */
+    private final static class RecMonitorClient implements IBinder.DeathRecipient {
+
+        // can afford to be static because only one RecordingActivityMonitor ever instantiated
+        static RecordingActivityMonitor sMonitor;
+
+        final IRecordingConfigDispatcher mDispatcherCb;
+
+        RecMonitorClient(IRecordingConfigDispatcher rcdb) {
+            mDispatcherCb = rcdb;
+        }
+
+        public void binderDied() {
+            Log.w(TAG, "client died");
+            sMonitor.unregisterRecordingCallback(mDispatcherCb);
+        }
+
+        boolean init() {
+            try {
+                mDispatcherCb.asBinder().linkToDeath(this, 0);
+                return true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Could not link to client death", e);
+                return false;
+            }
+        }
+
+        void release() {
+            mDispatcherCb.asBinder().unlinkToDeath(this, 0);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 3a10dbe..4504bdb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -223,7 +223,6 @@
     private final AlarmManager mAlarmManager;
     private final NetworkRequest mDefaultRequest;
 
-    private String mServer;
     private boolean mIsCaptivePortalCheckEnabled = false;
 
     // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
@@ -265,10 +264,6 @@
         addState(mLingeringState, mDefaultState);
         setInitialState(mDefaultState);
 
-        mServer = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_SERVER);
-        if (mServer == null) mServer = DEFAULT_SERVER;
-
         mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
         mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
@@ -622,6 +617,13 @@
         }
     }
 
+    public static String getCaptivePortalServerUrl(Context context) {
+        String server = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.CAPTIVE_PORTAL_SERVER);
+        if (server == null) server = DEFAULT_SERVER;
+        return "http://" + server + "/generate_204";
+    }
+
     /**
      * Do a URL fetch on a known server to see if we get the data we expect.
      * Returns HTTP response code.
@@ -633,9 +635,9 @@
         HttpURLConnection urlConnection = null;
         int httpResponseCode = 599;
         try {
-            URL url = new URL("http", mServer, "/generate_204");
+            URL url = new URL(getCaptivePortalServerUrl(mContext));
             // On networks with a PAC instead of fetching a URL that should result in a 204
-            // reponse, we instead simply fetch the PAC script.  This is done for a few reasons:
+            // response, we instead simply fetch the PAC script.  This is done for a few reasons:
             // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
             //    until something like https://android-review.googlesource.com/#/c/115180/ lands.
             //    Network.openConnection() will ignore network-specific PACs and instead fetch
@@ -644,7 +646,8 @@
             // 2. To proxy the generate_204 fetch through a PAC would require a number of things
             //    happen before the fetch can commence, namely:
             //        a) the PAC script be fetched
-            //        b) a PAC script resolver service be fired up and resolve mServer
+            //        b) a PAC script resolver service be fired up and resolve the captive portal
+            //           server.
             //    Network validation could be delayed until these prerequisities are satisifed or
             //    could simply be left to race them.  Neither is an optimal solution.
             // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2bea278..e957fc6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -63,6 +63,7 @@
 import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.text.TextUtils;
@@ -169,6 +170,58 @@
     }
 
     /**
+     * Configures an always-on VPN connection through a specific application.
+     * This connection is automatically granted and persisted after a reboot.
+     *
+     * <p>The designated package should exist and declare a {@link VpnService} in its
+     *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+     *    otherwise the call will fail.
+     *
+     * @param newPackage the package to designate as always-on VPN supplier.
+     */
+    public synchronized boolean setAlwaysOnPackage(String packageName) {
+        enforceControlPermissionOrInternalCaller();
+
+        // Disconnect current VPN.
+        prepareInternal(VpnConfig.LEGACY_VPN);
+
+        // Pre-authorize new always-on VPN package.
+        if (packageName != null) {
+            if (!setPackageAuthorization(packageName, true)) {
+                return false;
+            }
+        }
+
+        // Save the new package name in Settings.Secure.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ALWAYS_ON_VPN_APP, packageName, mUserHandle);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return true;
+    }
+
+    /**
+     * @return the package name of the VPN controller responsible for always-on VPN,
+     *         or {@code null} if none is set or always-on VPN is controlled through
+     *         lockdown instead.
+     * @hide
+     */
+    public synchronized String getAlwaysOnPackage() {
+        enforceControlPermissionOrInternalCaller();
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
      * Prepare for a VPN application. This method is designed to solve
      * race conditions. It first compares the current prepared package
      * with {@code oldPackage}. If they are the same, the prepared
@@ -270,14 +323,14 @@
     /**
      * Set whether a package has the ability to launch VPNs without user intervention.
      */
-    public void setPackageAuthorization(String packageName, boolean authorized) {
+    public boolean setPackageAuthorization(String packageName, boolean authorized) {
         // Check if the caller is authorized.
-        enforceControlPermission();
+        enforceControlPermissionOrInternalCaller();
 
         int uid = getAppUid(packageName, mUserHandle);
         if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
             // Authorization for nonexistent packages (or fake ones) can't be updated.
-            return;
+            return false;
         }
 
         long token = Binder.clearCallingIdentity();
@@ -286,11 +339,13 @@
                     (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
             appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
                     authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+            return true;
         } catch (Exception e) {
             Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+        return false;
     }
 
     private boolean isVpnUserPreConsented(String packageName) {
@@ -309,7 +364,7 @@
         PackageManager pm = mContext.getPackageManager();
         int result;
         try {
-            result = pm.getPackageUid(app, userHandle);
+            result = pm.getPackageUidAsUser(app, userHandle);
         } catch (NameNotFoundException e) {
             result = -1;
         }
@@ -743,6 +798,13 @@
         mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
     }
 
+    private void enforceControlPermissionOrInternalCaller() {
+        // Require caller to be either an application with CONTROL_VPN permission or a process
+        // in the system server.
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
+                "Unauthorized Caller");
+    }
+
     private class Connection implements ServiceConnection {
         private IBinder mService;
 
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 78618ce..2eb9095 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2682,6 +2682,31 @@
                         }
                         continue;
                     }
+                    String packageName = getPackageName(op.target);
+                    ApplicationInfo ai = null;
+                    if (packageName != null) {
+                        try {
+                            ai = mContext.getPackageManager().getApplicationInfo(packageName,
+                                    PackageManager.GET_UNINSTALLED_PACKAGES
+                                            | PackageManager.GET_DISABLED_COMPONENTS);
+                        } catch (NameNotFoundException e) {
+                            operationIterator.remove();
+                            mSyncStorageEngine.deleteFromPending(op.pendingOperation);
+                            continue;
+                        }
+                    }
+                    // If app is considered idle, then skip for now and backoff
+                    if (ai != null
+                            && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
+                        increaseBackoffSetting(op);
+                        op.appIdle = true;
+                        if (isLoggable) {
+                            Log.v(TAG, "Sync backing off idle app " + packageName);
+                        }
+                        continue;
+                    } else {
+                        op.appIdle = false;
+                    }
                     if (!isOperationValidLocked(op)) {
                         operationIterator.remove();
                         mSyncStorageEngine.deleteFromPending(op.pendingOperation);
@@ -2700,28 +2725,6 @@
                         }
                         continue;
                     }
-                    String packageName = getPackageName(op.target);
-                    ApplicationInfo ai = null;
-                    if (packageName != null) {
-                        try {
-                            ai = mContext.getPackageManager().getApplicationInfo(packageName,
-                                    PackageManager.GET_UNINSTALLED_PACKAGES
-                                    | PackageManager.GET_DISABLED_COMPONENTS);
-                        } catch (NameNotFoundException e) {
-                        }
-                    }
-                    // If app is considered idle, then skip for now and backoff
-                    if (ai != null
-                            && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
-                        increaseBackoffSetting(op);
-                        op.appIdle = true;
-                        if (isLoggable) {
-                            Log.v(TAG, "Sync backing off idle app " + packageName);
-                        }
-                        continue;
-                    } else {
-                        op.appIdle = false;
-                    }
                     // Add this sync to be run.
                     operations.add(op);
                 }
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 835ba17..a16fcd2 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -587,16 +587,16 @@
                     Slog.e(TAG, "Unable to create surface.", ex);
                     return false;
                 }
+
+                mSurfaceControl.setLayerStack(mDisplayLayerStack);
+                mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
+                mSurface = new Surface();
+                mSurface.copyFrom(mSurfaceControl);
+
+                mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+                        mDisplayId, mSurfaceControl);
+                mSurfaceLayout.onDisplayTransaction();
             }
-
-            mSurfaceControl.setLayerStack(mDisplayLayerStack);
-            mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
-            mSurface = new Surface();
-            mSurface.copyFrom(mSurfaceControl);
-
-            mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
-                    mDisplayId, mSurfaceControl);
-            mSurfaceLayout.onDisplayTransaction();
         } finally {
             SurfaceControl.closeTransaction();
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 433d887..b74b0f2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -673,6 +673,9 @@
                 slowChange = false;
             }
             mAppliedDimming = true;
+        } else if (mAppliedDimming) {
+            slowChange = false;
+            mAppliedDimming = false;
         }
 
         // If low power mode is enabled, cut the brightness level by half
@@ -685,6 +688,9 @@
                 slowChange = false;
             }
             mAppliedLowPower = true;
+        } else if (mAppliedLowPower) {
+            slowChange = false;
+            mAppliedLowPower = false;
         }
 
         // Animate the screen brightness when the screen is on or dozing.
diff --git a/services/core/java/com/android/server/firewall/SenderPackageFilter.java b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
index dc3edd9..91c9671 100644
--- a/services/core/java/com/android/server/firewall/SenderPackageFilter.java
+++ b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
 
@@ -46,7 +47,8 @@
         try {
             // USER_SYSTEM here is not important. Only app id is used and getPackageUid() will
             // return a uid whether the app is installed for a user or not.
-            packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_SYSTEM);
+            packageUid = pm.getPackageUid(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                    UserHandle.USER_SYSTEM);
         } catch (RemoteException ex) {
             // handled below
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index ee8aab6..ee91b63 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -70,7 +70,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
-import android.util.Log;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -97,9 +97,11 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 
 import libcore.io.Streams;
 import libcore.util.Objects;
@@ -720,40 +722,35 @@
         mTempInputDevicesChangedListenersToNotify.clear();
 
         // Check for missing keyboard layouts.
-        if (mNotificationManager != null) {
-            final int numFullKeyboards = mTempFullKeyboards.size();
-            boolean missingLayoutForExternalKeyboard = false;
-            boolean missingLayoutForExternalKeyboardAdded = false;
-            boolean multipleMissingLayoutsForExternalKeyboardsAdded = false;
-            InputDevice keyboardMissingLayout = null;
-            synchronized (mDataStore) {
-                for (int i = 0; i < numFullKeyboards; i++) {
-                    final InputDevice inputDevice = mTempFullKeyboards.get(i);
-                    final String layout =
-                            getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
-                    if (layout == null) {
-                        missingLayoutForExternalKeyboard = true;
-                        if (i < numFullKeyboardsAdded) {
-                            missingLayoutForExternalKeyboardAdded = true;
-                            if (keyboardMissingLayout == null) {
-                                keyboardMissingLayout = inputDevice;
-                            } else {
-                                multipleMissingLayoutsForExternalKeyboardsAdded = true;
-                            }
-                        }
+        List<InputDevice> keyboardsMissingLayout = new ArrayList<>();
+        final int numFullKeyboards = mTempFullKeyboards.size();
+        synchronized (mDataStore) {
+            for (int i = 0; i < numFullKeyboards; i++) {
+                final InputDevice inputDevice = mTempFullKeyboards.get(i);
+                String layout =
+                    getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
+                if (layout == null) {
+                    layout = getDefaultKeyboardLayout(inputDevice);
+                    if (layout != null) {
+                        setCurrentKeyboardLayoutForInputDevice(
+                                inputDevice.getIdentifier(), layout);
                     }
                 }
+                if (layout == null) {
+                    keyboardsMissingLayout.add(inputDevice);
+                }
             }
-            if (missingLayoutForExternalKeyboard) {
-                if (missingLayoutForExternalKeyboardAdded) {
-                    if (multipleMissingLayoutsForExternalKeyboardsAdded) {
-                        // We have more than one keyboard missing a layout, so drop the
-                        // user at the generic input methods page so they can pick which
-                        // one to set.
-                        showMissingKeyboardLayoutNotification(null);
-                    } else {
-                        showMissingKeyboardLayoutNotification(keyboardMissingLayout);
-                    }
+        }
+
+        if (mNotificationManager != null) {
+            if (!keyboardsMissingLayout.isEmpty()) {
+                if (keyboardsMissingLayout.size() > 1) {
+                    // We have more than one keyboard missing a layout, so drop the
+                    // user at the generic input methods page so they can pick which
+                    // one to set.
+                    showMissingKeyboardLayoutNotification(null);
+                } else {
+                    showMissingKeyboardLayoutNotification(keyboardsMissingLayout.get(0));
                 }
             } else if (mKeyboardLayoutNotificationShown) {
                 hideMissingKeyboardLayoutNotification();
@@ -762,6 +759,78 @@
         mTempFullKeyboards.clear();
     }
 
+    private String getDefaultKeyboardLayout(final InputDevice d) {
+        final Locale systemLocale = mContext.getResources().getConfiguration().locale;
+        // If our locale doesn't have a language for some reason, then we don't really have a
+        // reasonable default.
+        if (TextUtils.isEmpty(systemLocale.getLanguage())) {
+            return null;
+        }
+        final List<KeyboardLayout> layouts = new ArrayList<>();
+        visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
+            @Override
+            public void visitKeyboardLayout(Resources resources,
+                    int keyboardLayoutResId, KeyboardLayout layout) {
+                // Only select a default when we know the layout is appropriate. For now, this
+                // means its a custom layout for a specific keyboard.
+                if (layout.getVendorId() != d.getVendorId()
+                        || layout.getProductId() != d.getProductId()) {
+                    return;
+                }
+                for (Locale l : layout.getLocales()) {
+                    if (isCompatibleLocale(systemLocale, l)) {
+                        layouts.add(layout);
+                        break;
+                    }
+                }
+            }
+        });
+
+        if (layouts.isEmpty()) {
+            return null;
+        }
+
+        // First sort so that ones with higher priority are listed at the top
+        Collections.sort(layouts);
+        // Next we want to try to find an exact match of language, country and variant.
+        final int N = layouts.size();
+        for (int i = 0; i < N; i++) {
+            KeyboardLayout layout = layouts.get(i);
+            for (Locale l : layout.getLocales()) {
+                if (l.getCountry().equals(systemLocale.getCountry())
+                        && l.getVariant().equals(systemLocale.getVariant())) {
+                    return layout.getDescriptor();
+                }
+            }
+        }
+        // Then try an exact match of language and country
+        for (int i = 0; i < N; i++) {
+            KeyboardLayout layout = layouts.get(i);
+            for (Locale l : layout.getLocales()) {
+                if (l.getCountry().equals(systemLocale.getCountry())) {
+                    return layout.getDescriptor();
+                }
+            }
+        }
+
+        // Give up and just use the highest priority layout with matching language
+        return layouts.get(0).getDescriptor();
+    }
+
+    private static boolean isCompatibleLocale(Locale systemLocale, Locale keyboardLocale) {
+        // Different languages are never compatible
+        if (!systemLocale.getLanguage().equals(keyboardLocale.getLanguage())) {
+            return false;
+        }
+        // If both the system and the keyboard layout have a country specifier, they must be equal.
+        if (!TextUtils.isEmpty(systemLocale.getCountry())
+                && !TextUtils.isEmpty(keyboardLocale.getCountry())
+                && !systemLocale.getCountry().equals(keyboardLocale.getCountry())) {
+            return false;
+        }
+        return true;
+    }
+
     @Override // Binder call & native callback
     public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
             int surfaceRotation) {
@@ -911,9 +980,9 @@
         final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources, String descriptor, String label,
-                    String collection, int keyboardLayoutResId, int priority) {
-                availableKeyboardLayouts.add(descriptor);
+            public void visitKeyboardLayout(Resources resources,
+                    int keyboardLayoutResId, KeyboardLayout layout) {
+                availableKeyboardLayouts.add(layout.getDescriptor());
             }
         });
         synchronized (mDataStore) {
@@ -945,15 +1014,64 @@
         final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
         visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources, String descriptor, String label,
-                    String collection, int keyboardLayoutResId, int priority) {
-                list.add(new KeyboardLayout(descriptor, label, collection, priority));
+            public void visitKeyboardLayout(Resources resources,
+                    int keyboardLayoutResId, KeyboardLayout layout) {
+                list.add(layout);
             }
         });
         return list.toArray(new KeyboardLayout[list.size()]);
     }
 
     @Override // Binder call
+    public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
+            final InputDeviceIdentifier identifier) {
+        final String[] enabledLayoutDescriptors =
+            getEnabledKeyboardLayoutsForInputDevice(identifier);
+        final ArrayList<KeyboardLayout> enabledLayouts =
+            new ArrayList<KeyboardLayout>(enabledLayoutDescriptors.length);
+        final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<KeyboardLayout>();
+        visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
+            boolean mHasSeenDeviceSpecificLayout;
+
+            @Override
+            public void visitKeyboardLayout(Resources resources,
+                    int keyboardLayoutResId, KeyboardLayout layout) {
+                // First check if it's enabled. If the keyboard layout is enabled then we always
+                // want to return it as a possible layout for the device.
+                for (String s : enabledLayoutDescriptors) {
+                    if (s != null && s.equals(layout.getDescriptor())) {
+                        enabledLayouts.add(layout);
+                        return;
+                    }
+                }
+                // Next find any potential layouts that aren't yet enabled for the device. For
+                // devices that have special layouts we assume there's a reason that the generic
+                // layouts don't work for them so we don't want to return them since it's likely
+                // to result in a poor user experience.
+                if (layout.getVendorId() == identifier.getVendorId()
+                        && layout.getProductId() == identifier.getProductId()) {
+                    if (!mHasSeenDeviceSpecificLayout) {
+                        mHasSeenDeviceSpecificLayout = true;
+                        potentialLayouts.clear();
+                    }
+                    potentialLayouts.add(layout);
+                } else if (layout.getVendorId() == -1 && layout.getProductId() == -1
+                        && !mHasSeenDeviceSpecificLayout) {
+                    potentialLayouts.add(layout);
+                }
+            }
+        });
+        final int enabledLayoutSize = enabledLayouts.size();
+        final int potentialLayoutSize = potentialLayouts.size();
+        KeyboardLayout[] layouts = new KeyboardLayout[enabledLayoutSize + potentialLayoutSize];
+        enabledLayouts.toArray(layouts);
+        for (int i = 0; i < potentialLayoutSize; i++) {
+            layouts[enabledLayoutSize + i] = potentialLayouts.get(i);
+        }
+        return layouts;
+    }
+
+    @Override // Binder call
     public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) {
         if (keyboardLayoutDescriptor == null) {
             throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
@@ -962,13 +1080,13 @@
         final KeyboardLayout[] result = new KeyboardLayout[1];
         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources, String descriptor,
-                    String label, String collection, int keyboardLayoutResId, int priority) {
-                result[0] = new KeyboardLayout(descriptor, label, collection, priority);
+            public void visitKeyboardLayout(Resources resources,
+                    int keyboardLayoutResId, KeyboardLayout layout) {
+                result[0] = layout;
             }
         });
         if (result[0] == null) {
-            Log.w(TAG, "Could not get keyboard layout with descriptor '"
+            Slog.w(TAG, "Could not get keyboard layout with descriptor '"
                     + keyboardLayoutDescriptor + "'.");
         }
         return result[0];
@@ -1010,7 +1128,7 @@
 
         int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
         if (configResId == 0) {
-            Log.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
+            Slog.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
                     + "' on receiver " + receiver.packageName + "/" + receiver.name);
             return;
         }
@@ -1047,8 +1165,16 @@
                             int keyboardLayoutResId = a.getResourceId(
                                     com.android.internal.R.styleable.KeyboardLayout_keyboardLayout,
                                     0);
+                            String languageTags = a.getString(
+                                    com.android.internal.R.styleable.KeyboardLayout_locale);
+                            Locale[] locales = getLocalesFromLanguageTags(languageTags);
+                            int vid = a.getInt(
+                                    com.android.internal.R.styleable.KeyboardLayout_vendorId, -1);
+                            int pid = a.getInt(
+                                    com.android.internal.R.styleable.KeyboardLayout_productId, -1);
+
                             if (name == null || label == null || keyboardLayoutResId == 0) {
-                                Log.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
+                                Slog.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
                                         + "attributes in keyboard layout "
                                         + "resource from receiver "
                                         + receiver.packageName + "/" + receiver.name);
@@ -1056,15 +1182,18 @@
                                 String descriptor = KeyboardLayoutDescriptor.format(
                                         receiver.packageName, receiver.name, name);
                                 if (keyboardName == null || name.equals(keyboardName)) {
-                                    visitor.visitKeyboardLayout(resources, descriptor,
-                                            label, collection, keyboardLayoutResId, priority);
+                                    KeyboardLayout layout = new KeyboardLayout(
+                                            descriptor, label, collection, priority,
+                                            locales, vid, pid);
+                                    visitor.visitKeyboardLayout(
+                                            resources, keyboardLayoutResId, layout);
                                 }
                             }
                         } finally {
                             a.recycle();
                         }
                     } else {
-                        Log.w(TAG, "Skipping unrecognized element '" + element
+                        Slog.w(TAG, "Skipping unrecognized element '" + element
                                 + "' in keyboard layout resource from receiver "
                                 + receiver.packageName + "/" + receiver.name);
                     }
@@ -1073,11 +1202,23 @@
                 parser.close();
             }
         } catch (Exception ex) {
-            Log.w(TAG, "Could not parse keyboard layout resource from receiver "
+            Slog.w(TAG, "Could not parse keyboard layout resource from receiver "
                     + receiver.packageName + "/" + receiver.name, ex);
         }
     }
 
+    private static Locale[] getLocalesFromLanguageTags(String languageTags) {
+        if (TextUtils.isEmpty(languageTags)) {
+            return new Locale[0];
+        }
+        String[] tags = languageTags.split("\\|");
+        Locale[] locales = new Locale[tags.length];
+        for (int i = 0; i < tags.length; i++) {
+            locales[i] = Locale.forLanguageTag(tags[i]);
+        }
+        return locales;
+    }
+
     /**
      * Builds a layout descriptor for the vendor/product. This returns the
      * descriptor for ids that aren't useful (such as the default 0, 0).
@@ -1143,7 +1284,7 @@
     }
 
     @Override // Binder call
-    public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+    public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
         String key = getLayoutDescriptor(identifier);
         synchronized (mDataStore) {
             String[] layouts = mDataStore.getKeyboardLayouts(key);
@@ -1718,10 +1859,10 @@
         final String[] result = new String[2];
         visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
             @Override
-            public void visitKeyboardLayout(Resources resources, String descriptor, String label,
-                    String collection, int keyboardLayoutResId, int priority) {
+            public void visitKeyboardLayout(Resources resources,
+                    int keyboardLayoutResId, KeyboardLayout layout) {
                 try {
-                    result[0] = descriptor;
+                    result[0] = layout.getDescriptor();
                     result[1] = Streams.readFully(new InputStreamReader(
                             resources.openRawResource(keyboardLayoutResId)));
                 } catch (IOException ex) {
@@ -1730,7 +1871,7 @@
             }
         });
         if (result[0] == null) {
-            Log.w(TAG, "Could not get keyboard layout with descriptor '"
+            Slog.w(TAG, "Could not get keyboard layout with descriptor '"
                     + keyboardLayoutDescriptor + "'.");
             return null;
         }
@@ -1883,8 +2024,8 @@
     }
 
     private interface KeyboardLayoutVisitor {
-        void visitKeyboardLayout(Resources resources, String descriptor, String label,
-                String collection, int keyboardLayoutResId, int priority);
+        void visitKeyboardLayout(Resources resources,
+                int keyboardLayoutResId, KeyboardLayout layout);
     }
 
     private final class InputDevicesChangedListenerRecord implements DeathRecipient {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index c5e6e7c..24519db 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -16,14 +16,21 @@
 
 package com.android.server.job;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
-import android.app.job.IJobScheduler;
 import android.app.job.JobInfo;
+import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
+import android.app.job.IJobScheduler;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -42,12 +49,12 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.Process;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.ArrayUtils;
 import com.android.server.DeviceIdleController;
 import com.android.server.LocalServices;
 import com.android.server.job.controllers.AppIdleController;
@@ -58,15 +65,6 @@
 import com.android.server.job.controllers.StateController;
 import com.android.server.job.controllers.TimeController;
 
-import libcore.util.EmptyArray;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
 /**
  * Responsible for taking jobs representing work to be performed by a client app, and determining
  * based on the criteria specified when that job should be run against the client application's
@@ -130,7 +128,7 @@
      */
     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
 
-    int[] mStartedUsers = EmptyArray.INT;
+    final ArrayList<Integer> mStartedUsers = new ArrayList<>();
 
     final JobHandler mHandler;
     final JobSchedulerStub mJobSchedulerStub;
@@ -182,7 +180,7 @@
                     || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
                 updateIdleMode(mPowerManager != null
                         ? (mPowerManager.isDeviceIdleMode()
-                                || mPowerManager.isLightDeviceIdleMode())
+                        || mPowerManager.isLightDeviceIdleMode())
                         : false);
             }
         }
@@ -205,20 +203,14 @@
 
     @Override
     public void onStartUser(int userHandle) {
-        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
-        // Let's kick any outstanding jobs for this user.
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
-    }
-
-    @Override
-    public void onUnlockUser(int userHandle) {
+        mStartedUsers.add(userHandle);
         // Let's kick any outstanding jobs for this user.
         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
     }
 
     @Override
     public void onStopUser(int userHandle) {
-        mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
+        mStartedUsers.remove(Integer.valueOf(userHandle));
     }
 
     /**
@@ -229,7 +221,14 @@
      * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
      */
     public int schedule(JobInfo job, int uId) {
+        return scheduleAsPackage(job, uId, null, -1);
+    }
+
+    public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId) {
         JobStatus jobStatus = new JobStatus(job, uId);
+        if (packageName != null) {
+            jobStatus.setSource(packageName, userId);
+        }
         cancelJob(uId, job.getId());
         try {
             if (ActivityManagerNative.getDefault().getAppStartMode(uId,
@@ -325,7 +324,7 @@
             // Remove from pending queue.
             mPendingJobs.remove(cancelled);
             // Cancel if running.
-            stopJobOnServiceContextLocked(cancelled);
+            stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
             reportActive();
         }
     }
@@ -353,7 +352,7 @@
                         JobServiceContext jsc = mActiveServices.get(i);
                         final JobStatus executing = jsc.getRunningJob();
                         if (executing != null) {
-                            jsc.cancelExecutingJob();
+                            jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
                         }
                     }
                 } else {
@@ -378,7 +377,7 @@
         if (mPendingJobs.size() <= 0) {
             for (int i=0; i<mActiveServices.size(); i++) {
                 JobServiceContext jsc = mActiveServices.get(i);
-                if (!jsc.isAvailable()) {
+                if (jsc.getRunningJob() != null) {
                     active = true;
                     break;
                 }
@@ -515,12 +514,12 @@
         return removed;
     }
 
-    private boolean stopJobOnServiceContextLocked(JobStatus job) {
+    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
         for (int i=0; i<mActiveServices.size(); i++) {
             JobServiceContext jsc = mActiveServices.get(i);
             final JobStatus executing = jsc.getRunningJob();
             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
-                jsc.cancelExecutingJob();
+                jsc.cancelExecutingJob(reason);
                 return true;
             }
         }
@@ -602,9 +601,10 @@
         if (periodicToReschedule.hasDeadlineConstraint()) {
             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
         }
-        long newEarliestRunTimeElapsed = elapsedNow + runEarly;
+        long flex = periodicToReschedule.getJob().getFlexMillis();
         long period = periodicToReschedule.getJob().getIntervalMillis();
-        long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
+        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
+        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
 
         if (DEBUG) {
             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
@@ -719,6 +719,7 @@
          */
         private void queueReadyJobsForExecutionLockedH() {
             ArraySet<JobStatus> jobs = mJobs.getJobs();
+            mPendingJobs.clear();
             if (DEBUG) {
                 Slog.d(TAG, "queuing all ready jobs for execution:");
             }
@@ -729,8 +730,9 @@
                         Slog.d(TAG, "    queued " + job.toShortString());
                     }
                     mPendingJobs.add(job);
-                } else if (isReadyToBeCancelledLocked(job)) {
-                    stopJobOnServiceContextLocked(job);
+                } else if (areJobConstraintsNotSatisfied(job)) {
+                    stopJobOnServiceContextLocked(job,
+                            JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
                 }
             }
             if (DEBUG) {
@@ -753,8 +755,9 @@
          * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
          */
         private void maybeQueueReadyJobsForExecutionLockedH() {
+            mPendingJobs.clear();
             int chargingCount = 0;
-            int idleCount = 0;
+            int idleCount =  0;
             int backoffCount = 0;
             int connectivityCount = 0;
             List<JobStatus> runnableJobs = null;
@@ -789,8 +792,9 @@
                         runnableJobs = new ArrayList<>();
                     }
                     runnableJobs.add(job);
-                } else if (isReadyToBeCancelledLocked(job)) {
-                    stopJobOnServiceContextLocked(job);
+                } else if (areJobConstraintsNotSatisfied(job)) {
+                    stopJobOnServiceContextLocked(job,
+                            JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
                 }
             }
             if (backoffCount > 0 ||
@@ -809,11 +813,6 @@
                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
                 }
             }
-            if (DEBUG) {
-                Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
-                connectivityCount + " charging=" + chargingCount + " tot=" +
-                        runnableJobs.size());
-            }
         }
 
         /**
@@ -822,31 +821,18 @@
          *      - It's not pending.
          *      - It's not already running on a JSC.
          *      - The user that requested the job is running.
-         *      - The component is enabled and runnable.
          */
         private boolean isReadyToBeExecutedLocked(JobStatus job) {
             final boolean jobReady = job.isReady();
             final boolean jobPending = mPendingJobs.contains(job);
             final boolean jobActive = isCurrentlyActiveLocked(job);
-
-            final int userId = job.getUserId();
-            final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
-            final boolean componentPresent;
-            try {
-                componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
-                        job.getServiceComponent(), PackageManager.MATCH_ENCRYPTION_DEFAULT,
-                        userId) != null);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
-            }
-
+            final boolean userRunning = mStartedUsers.contains(job.getUserId());
             if (DEBUG) {
                 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
                         + " ready=" + jobReady + " pending=" + jobPending
-                        + " active=" + jobActive + " userStarted=" + userStarted
-                        + " componentPresent=" + componentPresent);
+                        + " active=" + jobActive + " userRunning=" + userRunning);
             }
-            return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
+            return userRunning && jobReady && !jobPending && !jobActive;
         }
 
         /**
@@ -854,7 +840,7 @@
          *      - It's not ready
          *      - It's running on a JSC.
          */
-        private boolean isReadyToBeCancelledLocked(JobStatus job) {
+        private boolean areJobConstraintsNotSatisfied(JobStatus job) {
             return !job.isReady() && isCurrentlyActiveLocked(job);
         }
 
@@ -869,46 +855,121 @@
                     // If device is idle, we will not schedule jobs to run.
                     return;
                 }
-                Iterator<JobStatus> it = mPendingJobs.iterator();
                 if (DEBUG) {
                     Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
                 }
-                while (it.hasNext()) {
-                    JobStatus nextPending = it.next();
-                    JobServiceContext availableContext = null;
-                    for (int i=0; i<mActiveServices.size(); i++) {
-                        JobServiceContext jsc = mActiveServices.get(i);
-                        final JobStatus running = jsc.getRunningJob();
-                        if (running != null && running.matches(nextPending.getUid(),
-                                nextPending.getJobId())) {
-                            // Already running this job for this uId, skip.
-                            availableContext = null;
-                            break;
-                        }
-                        if (jsc.isAvailable()) {
-                            availableContext = jsc;
-                        }
-                    }
-                    if (availableContext != null) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "About to run job "
-                                    + nextPending.getJob().getService().toString());
-                        }
-                        if (!availableContext.executeRunnableJob(nextPending)) {
-                            if (DEBUG) {
-                                Slog.d(TAG, "Error executing " + nextPending);
-                            }
-                            mJobs.remove(nextPending);
-                        }
-                        it.remove();
-                    }
-                }
+                assignJobsToContextsH();
                 reportActive();
             }
         }
     }
 
     /**
+     * Takes jobs from pending queue and runs them on available contexts.
+     * If no contexts are available, preempts lower priority jobs to
+     * run higher priority ones.
+     * Lock on mJobs before calling this function.
+     */
+    private void assignJobsToContextsH() {
+        if (DEBUG) {
+            Slog.d(TAG, printPendingQueue());
+        }
+
+        // This array essentially stores the state of mActiveServices array.
+        // ith index stores the job present on the ith JobServiceContext.
+        // We manipulate this array until we arrive at what jobs should be running on
+        // what JobServiceContext.
+        JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+        // Indicates whether we need to act on this jobContext id
+        boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT];
+        int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+        for (int i=0; i<mActiveServices.size(); i++) {
+            contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob();
+            preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid();
+        }
+        if (DEBUG) {
+            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
+        }
+        Iterator<JobStatus> it = mPendingJobs.iterator();
+        while (it.hasNext()) {
+            JobStatus nextPending = it.next();
+
+            // If job is already running, go to next job.
+            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
+            if (jobRunningContext != -1) {
+                continue;
+            }
+
+            // Find a context for nextPending. The context should be available OR
+            // it should have lowest priority among all running jobs
+            // (sharing the same Uid as nextPending)
+            int minPriority = Integer.MAX_VALUE;
+            int minPriorityContextId = -1;
+            for (int i=0; i<mActiveServices.size(); i++) {
+                JobStatus job = contextIdToJobMap[i];
+                int preferredUid = preferredUidForContext[i];
+                if (job == null && (preferredUid == nextPending.getUid() ||
+                        preferredUid == JobServiceContext.NO_PREFERRED_UID) ) {
+                    minPriorityContextId = i;
+                    break;
+                }
+                if (job.getUid() != nextPending.getUid()) {
+                    continue;
+                }
+                if (job.getPriority() >= nextPending.getPriority()) {
+                    continue;
+                }
+                if (minPriority > nextPending.getPriority()) {
+                    minPriority = nextPending.getPriority();
+                    minPriorityContextId = i;
+                }
+            }
+            if (minPriorityContextId != -1) {
+                contextIdToJobMap[minPriorityContextId] = nextPending;
+                act[minPriorityContextId] = true;
+            }
+        }
+        if (DEBUG) {
+            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
+        }
+        for (int i=0; i<mActiveServices.size(); i++) {
+            boolean preservePreferredUid = false;
+            if (act[i]) {
+                JobStatus js = mActiveServices.get(i).getRunningJob();
+                if (js != null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
+                    }
+                    // preferredUid will be set to uid of currently running job.
+                    mActiveServices.get(i).preemptExecutingJob();
+                    preservePreferredUid = true;
+                } else {
+                    if (DEBUG) {
+                        Slog.d(TAG, "About to run job on context "
+                                + String.valueOf(i) + ", job: " + contextIdToJobMap[i]);
+                    }
+                    if (!mActiveServices.get(i).executeRunnableJob(contextIdToJobMap[i])) {
+                        Slog.d(TAG, "Error executing " + contextIdToJobMap[i]);
+                    }
+                    mPendingJobs.remove(contextIdToJobMap[i]);
+                }
+            }
+            if (!preservePreferredUid) {
+                mActiveServices.get(i).clearPreferredUid();
+            }
+        }
+    }
+
+    int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
+        for (int i=0; i<map.length; i++) {
+            if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
      * Binder stub trampoline implementation
      */
     final class JobSchedulerStub extends IJobScheduler.Stub {
@@ -924,8 +985,7 @@
             final IPackageManager pm = AppGlobals.getPackageManager();
             final ComponentName service = job.getService();
             try {
-                ServiceInfo si = pm.getServiceInfo(service, PackageManager.MATCH_ENCRYPTION_DEFAULT,
-                        UserHandle.getUserId(uid));
+                ServiceInfo si = pm.getServiceInfo(service, 0, UserHandle.getUserId(uid));
                 if (si == null) {
                     throw new IllegalArgumentException("No such service " + service);
                 }
@@ -989,6 +1049,25 @@
         }
 
         @Override
+        public int scheduleAsPackage(JobInfo job, String packageName, int userId)
+                throws RemoteException {
+            if (DEBUG) {
+                Slog.d(TAG, "Scheduling job: " + job.toString() + " on behalf of " + packageName);
+            }
+            final int uid = Binder.getCallingUid();
+            if (uid != Process.SYSTEM_UID) {
+                throw new IllegalArgumentException("Only system process is allowed"
+                        + "to set packageName");
+            }
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return JobSchedulerService.this.scheduleAsPackage(job, uid, packageName, userId);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
         public List<JobInfo> getAllPendingJobs() throws RemoteException {
             final int uid = Binder.getCallingUid();
 
@@ -1038,12 +1117,41 @@
                 Binder.restoreCallingIdentity(identityToken);
             }
         }
+    };
+
+    private String printContextIdToJobMap(JobStatus[] map, String initial) {
+        StringBuilder s = new StringBuilder(initial + ": ");
+        for (int i=0; i<map.length; i++) {
+            s.append("(")
+                    .append(map[i] == null? -1: map[i].getJobId())
+                    .append(map[i] == null? -1: map[i].getUid())
+                    .append(")" );
+        }
+        return s.toString();
+    }
+
+    private String printPendingQueue() {
+        StringBuilder s = new StringBuilder("Pending queue: ");
+        Iterator<JobStatus> it = mPendingJobs.iterator();
+        while (it.hasNext()) {
+            JobStatus js = it.next();
+            s.append("(")
+                    .append(js.getJob().getId())
+                    .append(", ")
+                    .append(js.getUid())
+                    .append(") ");
+        }
+        return s.toString();
     }
 
     void dumpInternal(PrintWriter pw) {
         final long now = SystemClock.elapsedRealtime();
         synchronized (mJobs) {
-            pw.println("Started users: " + Arrays.toString(mStartedUsers));
+            pw.print("Started users: ");
+            for (int i=0; i<mStartedUsers.size(); i++) {
+                pw.print("u" + mStartedUsers.get(i) + " ");
+            }
+            pw.println();
             pw.println("Registered jobs:");
             if (mJobs.size() > 0) {
                 ArraySet<JobStatus> jobs = mJobs.getJobs();
@@ -1059,15 +1167,12 @@
                 mControllers.get(i).dumpControllerState(pw);
             }
             pw.println();
-            pw.println("Pending:");
-            for (int i=0; i<mPendingJobs.size(); i++) {
-                pw.println(mPendingJobs.get(i).hashCode());
-            }
+            pw.println(printPendingQueue());
             pw.println();
             pw.println("Active jobs:");
             for (int i=0; i<mActiveServices.size(); i++) {
                 JobServiceContext jsc = mActiveServices.get(i);
-                if (jsc.isAvailable()) {
+                if (jsc.getRunningJob() == null) {
                     continue;
                 } else {
                     final long timeout = jsc.getTimeoutElapsed();
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 5376043..c359c4d 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -59,7 +60,6 @@
  * To mitigate this, tearing down the context removes all messages from the handler, including any
  * tardy {@link #MSG_CANCEL}s. Additionally, we avoid sending duplicate onStopJob()
  * calls to the client after they've specified jobFinished().
- *
  */
 public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
     private static final boolean DEBUG = JobSchedulerService.DEBUG;
@@ -95,6 +95,8 @@
     /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/
     private static final int MSG_SHUTDOWN_EXECUTION = 4;
 
+    public static final int NO_PREFERRED_UID = -1;
+
     private final Handler mCallbackHandler;
     /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
     private final JobCompletedListener mCompletedListener;
@@ -117,7 +119,8 @@
      * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
      */
     private JobStatus mRunningJob;
-    /** Binder to the client service. */
+    /** Used to store next job to run when current job is to be preempted. */
+    private int mPreferredUid;
     IJobService service;
 
     private final Object mLock = new Object();
@@ -138,17 +141,19 @@
 
     @VisibleForTesting
     JobServiceContext(Context context, IBatteryStats batteryStats,
-            JobCompletedListener completedListener, Looper looper) {
+                      JobCompletedListener completedListener, Looper looper) {
         mContext = context;
         mBatteryStats = batteryStats;
         mCallbackHandler = new JobServiceHandler(looper);
         mCompletedListener = completedListener;
         mAvailable = true;
+        mVerb = VERB_FINISHED;
+        mPreferredUid = NO_PREFERRED_UID;
     }
 
     /**
-     * Give a job to this context for execution. Callers must first check {@link #isAvailable()}
-     * to make sure this is a valid context.
+     * Give a job to this context for execution. Callers must first check {@link #getRunningJob()}
+     * and ensure it is null to make sure this is a valid context.
      * @param job The status of the job that we are going to run.
      * @return True if the job is valid and is running. False if the job cannot be executed.
      */
@@ -159,6 +164,8 @@
                 return false;
             }
 
+            mPreferredUid = NO_PREFERRED_UID;
+
             mRunningJob = job;
             final boolean isDeadlineExpired =
                     job.hasDeadlineConstraint() &&
@@ -184,7 +191,7 @@
                 return false;
             }
             try {
-                mBatteryStats.noteJobStart(job.getName(), job.getUid());
+                mBatteryStats.noteJobStart(job.getName(), job.getSourceUid());
             } catch (RemoteException e) {
                 // Whatever.
             }
@@ -206,17 +213,22 @@
     }
 
     /** Called externally when a job that was scheduled for execution should be cancelled. */
-    void cancelExecutingJob() {
-        mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
+    void cancelExecutingJob(int reason) {
+        mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
     }
 
-    /**
-     * @return Whether this context is available to handle incoming work.
-     */
-    boolean isAvailable() {
-        synchronized (mLock) {
-            return mAvailable;
-        }
+    void preemptExecutingJob() {
+        Message m = mCallbackHandler.obtainMessage(MSG_CANCEL);
+        m.arg1 = JobParameters.REASON_PREEMPT;
+        m.sendToTarget();
+    }
+
+    int getPreferredUid() {
+        return mPreferredUid;
+    }
+
+    void clearPreferredUid() {
+        mPreferredUid = NO_PREFERRED_UID;
     }
 
     long getExecutionStartTimeElapsed() {
@@ -344,6 +356,11 @@
                     }
                     break;
                 case MSG_CANCEL:
+                    mParams.setStopReason(message.arg1);
+                    if (message.arg1 == JobParameters.REASON_PREEMPT) {
+                        mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
+                                NO_PREFERRED_UID;
+                    }
                     handleCancelH();
                     break;
                 case MSG_TIMEOUT:
@@ -481,6 +498,7 @@
 
         /** Process MSG_TIMEOUT here. */
         private void handleOpTimeoutH() {
+            mParams.setStopReason(JobParameters.REASON_TIMEOUT);
             switch (mVerb) {
                 case VERB_BINDING:
                     Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
@@ -549,7 +567,7 @@
                 }
                 completedJob = mRunningJob;
                 try {
-                    mBatteryStats.noteJobFinish(mRunningJob.getName(), mRunningJob.getUid());
+                    mBatteryStats.noteJobFinish(mRunningJob.getName(), mRunningJob.getSourceUid());
                 } catch (RemoteException e) {
                     // Whatever.
                 }
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 472e8f6..c88f5d7 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -312,7 +312,7 @@
                         Slog.d(TAG, "Saving job " + jobStatus.getJobId());
                     }
                     out.startTag(null, "job");
-                    addIdentifierAttributesToJobTag(out, jobStatus);
+                    addAttributesToJobTag(out, jobStatus);
                     writeConstraintsToXml(out, jobStatus);
                     writeExecutionCriteriaToXml(out, jobStatus);
                     writeBundleToXml(jobStatus.getExtras(), out);
@@ -337,13 +337,20 @@
             }
         }
 
-        /** Write out a tag with data comprising the required fields of this job and its client. */
-        private void addIdentifierAttributesToJobTag(XmlSerializer out, JobStatus jobStatus)
+        /** Write out a tag with data comprising the required fields and priority of this job and
+         * its client.
+         */
+        private void addAttributesToJobTag(XmlSerializer out, JobStatus jobStatus)
                 throws IOException {
             out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId()));
             out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName());
             out.attribute(null, "class", jobStatus.getServiceComponent().getClassName());
+            if (jobStatus.getSourcePackageName() != null) {
+                out.attribute(null, "sourcePackageName", jobStatus.getSourcePackageName());
+            }
+            out.attribute(null, "sourceUserId", String.valueOf(jobStatus.getSourceUserId()));
             out.attribute(null, "uid", Integer.toString(jobStatus.getUid()));
+            out.attribute(null, "priority", String.valueOf(jobStatus.getPriority()));
         }
 
         private void writeBundleToXml(PersistableBundle extras, XmlSerializer out)
@@ -361,9 +368,9 @@
             PersistableBundle copy = (PersistableBundle) bundle.clone();
             Set<String> keySet = bundle.keySet();
             for (String key: keySet) {
-                PersistableBundle b = copy.getPersistableBundle(key);
-                if (b != null) {
-                    PersistableBundle bCopy = deepCopyBundle(b, maxDepth-1);
+                Object o = copy.get(key);
+                if (o instanceof PersistableBundle) {
+                    PersistableBundle bCopy = deepCopyBundle((PersistableBundle) o, maxDepth-1);
                     copy.putPersistableBundle(key, bCopy);
                 }
             }
@@ -397,6 +404,7 @@
             if (jobStatus.getJob().isPeriodic()) {
                 out.startTag(null, XML_TAG_PERIODIC);
                 out.attribute(null, "period", Long.toString(job.getIntervalMillis()));
+                out.attribute(null, "flex", Long.toString(job.getFlexMillis()));
             } else {
                 out.startTag(null, XML_TAG_ONEOFF);
             }
@@ -538,18 +546,27 @@
         private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             JobInfo.Builder jobBuilder;
-            int uid;
+            int uid, userId;
 
-            // Read out job identifier attributes.
+            // Read out job identifier attributes and priority.
             try {
                 jobBuilder = buildBuilderFromXml(parser);
                 jobBuilder.setPersisted(true);
                 uid = Integer.valueOf(parser.getAttributeValue(null, "uid"));
+
+                String val = parser.getAttributeValue(null, "priority");
+                if (val != null) {
+                    jobBuilder.setPriority(Integer.valueOf(val));
+                }
+                val = parser.getAttributeValue(null, "sourceUserId");
+                userId = val == null ? -1 : Integer.valueOf(val);
             } catch (NumberFormatException e) {
                 Slog.e(TAG, "Error parsing job's required fields, skipping");
                 return null;
             }
 
+            final String sourcePackageName = parser.getAttributeValue(null, "sourcePackageName");
+
             int eventType;
             // Read out constraints tag.
             do {
@@ -594,13 +611,17 @@
                     String val = parser.getAttributeValue(null, "period");
                     final long periodMillis = Long.valueOf(val);
                     jobBuilder.setPeriodic(periodMillis);
-                    // As a sanity check, cap the recreated run time to be no later than 2 periods
+                    val = parser.getAttributeValue(null, "flex");
+                    final long flexMillis = (val != null) ? Long.valueOf(val) : periodMillis;
+                    // As a sanity check, cap the recreated run time to be no later than flex+period
                     // from now. This is the latest the periodic could be pushed out. This could
-                    // happen if the periodic ran early (at the start of its period), and then the
+                    // happen if the periodic ran early (at flex time before period), and then the
                     // device rebooted.
-                    if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) {
-                        final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis;
-                        final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis;
+                    if (elapsedRuntimes.second > elapsedNow + periodMillis + flexMillis) {
+                        final long clampedLateRuntimeElapsed = elapsedNow + flexMillis
+                                + periodMillis;
+                        final long clampedEarlyRuntimeElapsed = clampedLateRuntimeElapsed
+                                - flexMillis;
                         Slog.w(TAG,
                                 String.format("Periodic job for uid='%d' persisted run-time is" +
                                                 " too big [%s, %s]. Clamping to [%s,%s]",
@@ -659,8 +680,12 @@
             jobBuilder.setExtras(extras);
             parser.nextTag(); // Consume </extras>
 
-            return new JobStatus(
+            JobStatus js = new JobStatus(
                     jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second);
+            if (userId != -1) {
+                js.setSource(sourcePackageName, userId);
+            }
+            return js;
         }
 
         private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 6fc02f6..c09e06c 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -65,9 +65,9 @@
     public void maybeStartTrackingJob(JobStatus jobStatus) {
         synchronized (mTrackedTasks) {
             mTrackedTasks.add(jobStatus);
-            String packageName = jobStatus.job.getService().getPackageName();
+            String packageName = jobStatus.getSourcePackageName();
             final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                    jobStatus.uId, jobStatus.getUserId());
+                    jobStatus.getSourceUid(), jobStatus.getSourceUserId());
             if (DEBUG) {
                 Slog.d(LOG_TAG, "Start tracking, setting idle state of "
                         + packageName + " to " + appIdle);
@@ -89,7 +89,7 @@
         pw.println("Parole On: " + mAppIdleParoleOn);
         synchronized (mTrackedTasks) {
             for (JobStatus task : mTrackedTasks) {
-                pw.print(task.job.getService().getPackageName());
+                pw.print(task.getSourcePackageName());
                 pw.print(":idle=" + !task.appNotIdleConstraintSatisfied.get());
                 pw.print(", ");
             }
@@ -106,9 +106,9 @@
             }
             mAppIdleParoleOn = isAppIdleParoleOn;
             for (JobStatus task : mTrackedTasks) {
-                String packageName = task.job.getService().getPackageName();
+                String packageName = task.getSourcePackageName();
                 final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                        task.uId, task.getUserId());
+                        task.getSourceUid(), task.getSourceUserId());
                 if (DEBUG) {
                     Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
                 }
@@ -133,8 +133,8 @@
                     return;
                 }
                 for (JobStatus task : mTrackedTasks) {
-                    if (task.job.getService().getPackageName().equals(packageName)
-                            && task.getUserId() == userId) {
+                    if (task.getSourcePackageName().equals(packageName)
+                            && task.getSourceUserId() == userId) {
                         if (task.appNotIdleConstraintSatisfied.get() != !idle) {
                             if (DEBUG) {
                                 Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 92df851..fe5e8c9 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -34,14 +34,13 @@
 public class IdleController extends StateController {
     private static final String TAG = "IdleController";
 
-    // Policy: we decide that we're "idle" if the device has been unused /
-    // screen off or dreaming for at least this long
-    private static final long INACTIVITY_IDLE_THRESHOLD = 71 * 60 * 1000; // millis; 71 min
-    private static final long IDLE_WINDOW_SLOP = 5 * 60 * 1000; // 5 minute window, to be nice
-
     private static final String ACTION_TRIGGER_IDLE =
             "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE";
 
+    // Policy: we decide that we're "idle" if the device has been unused /
+    // screen off or dreaming for at least this long
+    private long mInactivityIdleThreshold;
+    private long mIdleWindowSlop;
     final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
     IdlenessTracker mIdleTracker;
 
@@ -100,6 +99,10 @@
      * significant state changes occur
      */
     private void initIdleStateTracking() {
+        mInactivityIdleThreshold = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+        mIdleWindowSlop = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
         mIdleTracker = new IdlenessTracker();
         mIdleTracker.startTracking();
     }
@@ -168,14 +171,14 @@
                 // alarm that will tell us when we have decided the device is
                 // truly idle.
                 final long nowElapsed = SystemClock.elapsedRealtime();
-                final long when = nowElapsed + INACTIVITY_IDLE_THRESHOLD;
+                final long when = nowElapsed + mInactivityIdleThreshold;
                 if (DEBUG) {
                     Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
                             + when);
                 }
                 mScreenOn = false;
                 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                        when, IDLE_WINDOW_SLOP, mIdleTriggerIntent);
+                        when, mIdleWindowSlop, mIdleTriggerIntent);
             } else if (action.equals(ACTION_TRIGGER_IDLE)) {
                 // idle time starts now. Do not set mIdle if screen is on.
                 if (!mIdle && !mScreenOn) {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index c02611f..a621e6a 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -16,9 +16,11 @@
 
 package com.android.server.job.controllers;
 
+import android.app.AppGlobals;
 import android.app.job.JobInfo;
 import android.content.ComponentName;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateUtils;
@@ -47,6 +49,10 @@
     final String name;
     final String tag;
 
+    String sourcePackageName;
+    int sourceUserId = -1;
+    int sourceUid = -1;
+
     // Constraints.
     final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
     final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
@@ -77,6 +83,7 @@
     private JobStatus(JobInfo job, int uId, int numFailures) {
         this.job = job;
         this.uId = uId;
+        this.sourceUid = uId;
         this.name = job.getService().flattenToShortString();
         this.tag = "*job*/" + this.name;
         this.numFailures = numFailures;
@@ -96,8 +103,8 @@
         final long elapsedNow = SystemClock.elapsedRealtime();
 
         if (job.isPeriodic()) {
-            earliestRunTimeElapsedMillis = elapsedNow;
             latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
+            earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
         } else {
             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
@@ -146,6 +153,21 @@
         return job.getService();
     }
 
+    public String getSourcePackageName() {
+        return sourcePackageName != null ? sourcePackageName : job.getService().getPackageName();
+    }
+
+    public int getSourceUid() {
+        return sourceUid;
+    }
+
+    public int getSourceUserId() {
+        if (sourceUserId == -1) {
+            sourceUserId = getUserId();
+        }
+        return sourceUserId;
+    }
+
     public int getUserId() {
         return UserHandle.getUserId(uId);
     }
@@ -166,6 +188,10 @@
         return job.getExtras();
     }
 
+    public int getPriority() {
+        return job.getPriority();
+    }
+
     public boolean hasConnectivityConstraint() {
         return job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY;
     }
@@ -259,6 +285,22 @@
         }
     }
 
+    public void setSource(String sourcePackageName, int sourceUserId) {
+        this.sourcePackageName = sourcePackageName;
+        this.sourceUserId = sourceUserId;
+        try {
+            sourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
+                    sourceUserId);
+        } catch (RemoteException ex) {
+            // Can't happen, PackageManager runs in the same process.
+        }
+        if (sourceUid == -1) {
+            sourceUid = uId;
+            this.sourceUserId = getUserId();
+            this.sourcePackageName = null;
+        }
+    }
+
     /**
      * Convenience function to identify a job uniquely without pulling all the data that
      * {@link #toString()} returns.
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
new file mode 100644
index 0000000..50dd607
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
@@ -0,0 +1,103 @@
+/*
+ * 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.server.media;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.media.IMediaResourceMonitor;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
+
+import java.util.List;
+
+/** This class provides a system service that monitors media resource usage. */
+public class MediaResourceMonitorService extends SystemService {
+    private static final String TAG = "MediaResourceMonitor";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final String SERVICE_NAME = "media_resource_monitor";
+
+    /*
+     *  Resource types. Should be in sync with:
+     *  frameworks/av/media/libmedia/MediaResource.cpp
+     */
+    private static final String RESOURCE_AUDIO_CODEC = "audio-codec";
+    private static final String RESOURCE_VIDEO_CODEC = "video-codec";
+
+    private final MediaResourceMonitorImpl mMediaResourceMonitorImpl;
+
+    public MediaResourceMonitorService(Context context) {
+        super(context);
+        mMediaResourceMonitorImpl = new MediaResourceMonitorImpl();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(SERVICE_NAME, mMediaResourceMonitorImpl);
+    }
+
+    class MediaResourceMonitorImpl extends IMediaResourceMonitor.Stub {
+        @Override
+        public void notifyResourceGranted(int pid, String type, String subType, long value)
+                throws RemoteException {
+            if (DEBUG) {
+                Slog.d(TAG, "notifyResourceGranted(pid=" + pid + ", type=" + type + ", subType="
+                        + subType + ", value=" + value + ")");
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                String pkgNames[] = getPackageNamesFromPid(pid);
+                Integer resourceType = null;
+                if (RESOURCE_AUDIO_CODEC.equals(subType)) {
+                    resourceType = Intent.EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC;
+                } else if (RESOURCE_VIDEO_CODEC.equals(subType)) {
+                    resourceType = Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC;
+                }
+                if (pkgNames != null && resourceType != null) {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+                    intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
+                    intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, resourceType);
+                    getContext().sendBroadcastAsUser(intent,
+                            new UserHandle(ActivityManager.getCurrentUser()),
+                            android.Manifest.permission.RECEIVE_MEDIA_RESOURCE_USAGE);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        private String[] getPackageNamesFromPid(int pid) {
+            try {
+                for (ActivityManager.RunningAppProcessInfo proc :
+                        ActivityManagerNative.getDefault().getRunningAppProcesses()) {
+                    if (proc.pid == pid) {
+                        return proc.pkgList;
+                    }
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "ActivityManager.getRunningAppProcesses() failed");
+            }
+            return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d5c3113..745f476 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -767,7 +767,7 @@
                 synchronized (mLock) {
                     // If we don't have a media button receiver to fall back on
                     // include non-playing sessions for dispatching
-                    UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
+                    UserRecord ur = mUserRecords.get(mCurrentUserId);
                     boolean useNotPlayingSessions = (ur == null) ||
                             (ur.mLastMediaButtonReceiver == null
                                 && ur.mRestoredMediaButtonReceiver == null);
@@ -949,8 +949,7 @@
                         mKeyEventReceiver);
             } else {
                 // Launch the last PendingIntent we had with priority
-                int userId = ActivityManager.getCurrentUser();
-                UserRecord user = mUserRecords.get(userId);
+                UserRecord user = mUserRecords.get(mCurrentUserId);
                 if (user != null && (user.mLastMediaButtonReceiver != null
                         || user.mRestoredMediaButtonReceiver != null)) {
                     if (DEBUG) {
@@ -967,11 +966,11 @@
                         if (user.mLastMediaButtonReceiver != null) {
                             user.mLastMediaButtonReceiver.send(getContext(),
                                     needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                                    mediaButtonIntent, mKeyEventReceiver, null);
+                                    mediaButtonIntent, mKeyEventReceiver, mHandler);
                         } else {
                             mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
                             getContext().sendBroadcastAsUser(mediaButtonIntent,
-                                    new UserHandle(userId));
+                                    new UserHandle(mCurrentUserId));
                         }
                     } catch (CanceledException e) {
                         Log.i(TAG, "Error sending key event to media button receiver "
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 1997e40..4764300 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2234,9 +2234,11 @@
         final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         final int userId = UserHandle.getUserId(uid);
 
-        for (String packageName : packages) {
-            if (!mUsageStats.isAppIdle(packageName, uid, userId)) {
-                return false;
+        if (!ArrayUtils.isEmpty(packages)) {
+            for (String packageName : packages) {
+                if (!mUsageStats.isAppIdle(packageName, uid, userId)) {
+                    return false;
+                }
             }
         }
         return true;
@@ -2333,7 +2335,8 @@
         @Override
         public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
             try {
-                int uid = mContext.getPackageManager().getPackageUid(packageName, userId);
+                final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
                 synchronized (mRulesLock) {
                     updateRuleForAppIdleLocked(uid);
                 }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 1987214..ce18818 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -121,6 +121,11 @@
     }
 
     @Override
+    protected boolean checkType(IInterface service) {
+        return service instanceof IConditionProvider;
+    }
+
+    @Override
     public void onBootPhaseAppsCanStart() {
         super.onBootPhaseAppsCanStart();
         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7662da..f360dc2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -86,7 +86,7 @@
     protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();
     // things that will be put into mServices as soon as they're ready
     private final ArrayList<String> mServicesBinding = new ArrayList<String>();
-    // lists the component names of all enabled (and therefore connected)
+    // lists the component names of all enabled (and therefore potentially connected)
     // app services for current profiles.
     private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
             = new ArraySet<ComponentName>();
@@ -97,6 +97,8 @@
     private ArraySet<String> mRestoredPackages = new ArraySet<>();
     // State of current service categories
     private ArrayMap<String, Boolean> mCategoryEnabled = new ArrayMap<>();
+    // List of enabled packages that have nevertheless asked not to be run
+    private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
 
 
     // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
@@ -141,6 +143,8 @@
 
     abstract protected IInterface asInterface(IBinder binder);
 
+    abstract protected boolean checkType(IInterface service);
+
     abstract protected void onServiceAdded(ManagedServiceInfo info);
 
     protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
@@ -169,7 +173,14 @@
             if (filter != null && !filter.matches(info.component)) continue;
             pw.println("      " + info.component
                     + " (user " + info.userid + "): " + info.service
-                    + (info.isSystem?" SYSTEM":""));
+                    + (info.isSystem?" SYSTEM":"")
+                    + (info.isGuest(this)?" GUEST":""));
+        }
+
+        pw.println("    Snoozed " + getCaption() + "s (" +
+                mSnoozingForCurrentProfiles.size() + "):");
+        for (ComponentName name : mSnoozingForCurrentProfiles) {
+            pw.println("      " + name.flattenToShortString());
         }
     }
 
@@ -239,14 +250,25 @@
         rebindServices();
     }
 
-    public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
-        checkNotNull(service);
+    public ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
+        if (service == null) {
+            return null;
+        }
         final IBinder token = service.asBinder();
         final int N = mServices.size();
         for (int i = 0; i < N; i++) {
             final ManagedServiceInfo info = mServices.get(i);
             if (info.service.asBinder() == token) return info;
         }
+        return null;
+    }
+
+    public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
+        checkNotNull(service);
+        ManagedServiceInfo info = getServiceFromTokenLocked(service);
+        if (info != null) {
+            return info;
+        }
         throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
                 + service);
     }
@@ -266,6 +288,47 @@
         }
     }
 
+    /**
+     * Add a service to our callbacks. The lifecycle of this service is managed externally,
+     * but unlike a system service, it should not be considered privledged.
+     * */
+    public void registerGuestService(ManagedServiceInfo guest) {
+        checkNotNull(guest.service);
+        checkType(guest.service);
+        if (registerServiceImpl(guest) != null) {
+            onServiceAdded(guest);
+            onServiceAdded(guest);
+        }
+    }
+
+    public void setComponentState(ComponentName component, boolean enabled) {
+        boolean previous = !mSnoozingForCurrentProfiles.contains(component);
+        if (previous == enabled) {
+            return;
+        }
+
+        if (enabled) {
+            mSnoozingForCurrentProfiles.remove(component);
+        } else {
+            mSnoozingForCurrentProfiles.add(component);
+        }
+
+        // State changed
+        if (DEBUG) {
+            Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " +
+                    component.flattenToShortString());
+        }
+
+        final int[] userIds = mUserProfiles.getCurrentProfileIds();
+        for (int userId : userIds) {
+            if (enabled) {
+                registerServiceLocked(component, userId);
+            } else {
+                unregisterServiceLocked(component, userId);
+            }
+        }
+    }
+
     public void setCategoryState(String category, boolean enabled) {
         synchronized (mMutex) {
             final Boolean previous = mCategoryEnabled.put(category, enabled);
@@ -309,6 +372,7 @@
 
     private void rebuildRestoredPackages() {
         mRestoredPackages.clear();
+        mSnoozingForCurrentProfiles.clear();
         String settingName = restoredSettingName(mConfig);
         int[] userIds = mUserProfiles.getCurrentProfileIds();
         final int N = userIds.length;
@@ -484,7 +548,7 @@
         synchronized (mMutex) {
             // Unbind automatically bound services, retain system services.
             for (ManagedServiceInfo service : mServices) {
-                if (!service.isSystem) {
+                if (!service.isSystem && !service.isGuest(this)) {
                     toRemove.add(service);
                 }
             }
@@ -510,6 +574,7 @@
                         add.removeAll(c);
                     }
                 }
+                add.removeAll(mSnoozingForCurrentProfiles);
 
                 toAdd.put(userIds[i], add);
 
@@ -709,11 +774,15 @@
 
     private ManagedServiceInfo registerServiceImpl(final IInterface service,
             final ComponentName component, final int userid) {
+        ManagedServiceInfo info = newServiceInfo(service, component, userid,
+                true /*isSystem*/, null /*connection*/, Build.VERSION_CODES.LOLLIPOP);
+        return registerServiceImpl(info);
+    }
+
+    private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
         synchronized (mMutex) {
             try {
-                ManagedServiceInfo info = newServiceInfo(service, component, userid,
-                        true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
-                service.asBinder().linkToDeath(info, 0);
+                info.service.asBinder().linkToDeath(info, 0);
                 mServices.add(info);
                 return info;
             } catch (RemoteException e) {
@@ -728,7 +797,7 @@
      */
     private void unregisterServiceImpl(IInterface service, int userid) {
         ManagedServiceInfo info = removeServiceImpl(service, userid);
-        if (info != null && info.connection != null) {
+        if (info != null && info.connection != null && !info.isGuest(this)) {
             mContext.unbindService(info.connection);
         }
     }
@@ -780,6 +849,14 @@
             this.targetSdkVersion = targetSdkVersion;
         }
 
+        public boolean isGuest(ManagedServices host) {
+            return ManagedServices.this != host;
+        }
+
+        public ManagedServices getOwner() {
+            return ManagedServices.this;
+        }
+
         @Override
         public String toString() {
             return new StringBuilder("ManagedServiceInfo[")
@@ -823,6 +900,11 @@
         }
     }
 
+    /** convenience method for looking in mEnabledServicesForCurrentProfiles */
+    public boolean isComponentEnabledForCurrentProfiles(ComponentName component) {
+        return mEnabledServicesForCurrentProfiles.contains(component);
+    }
+
     public static class UserProfiles {
         // Profiles of the current user.
         private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 32db000..5e4703d 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -18,20 +18,13 @@
 import java.util.Comparator;
 
 /**
- * Sorts notifications individually into attention-relelvant order.
+ * Sorts notifications individually into attention-relevant order.
  */
 public class NotificationComparator
         implements Comparator<NotificationRecord> {
 
     @Override
     public int compare(NotificationRecord left, NotificationRecord right) {
-        final int leftPackagePriority = left.getPackagePriority();
-        final int rightPackagePriority = right.getPackagePriority();
-        if (leftPackagePriority != rightPackagePriority) {
-            // by priority, high to low
-            return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
-        }
-
         final int leftImportance = left.getImportance();
         final int rightImportance = right.getImportance();
         if (leftImportance != rightImportance) {
@@ -39,6 +32,13 @@
             return -1 * Integer.compare(leftImportance, rightImportance);
         }
 
+        final int leftPackagePriority = left.getPackagePriority();
+        final int rightPackagePriority = right.getPackagePriority();
+        if (leftPackagePriority != rightPackagePriority) {
+            // by priority, high to low
+            return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
+        }
+
         final float leftPeople = left.getContactAffinity();
         final float rightPeople = right.getContactAffinity();
         if (leftPeople != rightPeople) {
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index d4fadcf..b57cc75 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -18,6 +18,7 @@
 
 import android.app.Notification;
 import android.content.Context;
+import android.service.notification.NotificationListenerService;
 import android.util.Log;
 import android.util.Slog;
 
@@ -44,11 +45,7 @@
         }
 
         final Notification notification = record.getNotification();
-        if ((notification.defaults & Notification.DEFAULT_VIBRATE) != 0 ||
-                notification.vibrate != null ||
-                (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
-                notification.sound != null ||
-                notification.fullScreenIntent != null) {
+        if (record.getImportance() > NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) {
             record.setRecentlyIntrusive(true);
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 49aa73e..b1fe68c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -28,17 +28,20 @@
 import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
 import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
 import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationAssistantService.REASON_TOPIC_BANNED;
 import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_LIGHTS;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_PEEK;
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
@@ -96,6 +99,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.NotificationAssistantService;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
@@ -154,6 +158,7 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 /** {@hide} */
 public class NotificationManagerService extends SystemService {
@@ -167,11 +172,13 @@
     // message codes
     static final int MESSAGE_TIMEOUT = 2;
     static final int MESSAGE_SAVE_POLICY_FILE = 3;
-    static final int MESSAGE_RECONSIDER_RANKING = 4;
-    static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
-    static final int MESSAGE_SEND_RANKING_UPDATE = 6;
-    static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
-    static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
+    static final int MESSAGE_SEND_RANKING_UPDATE = 4;
+    static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
+    static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
+
+    // ranking thread messages
+    private static final int MESSAGE_RECONSIDER_RANKING = 1000;
+    private static final int MESSAGE_RANKING_SORT = 1001;
 
     static final int LONG_DELAY = 3500; // 3.5 seconds
     static final int SHORT_DELAY = 2000; // 2 seconds
@@ -281,11 +288,13 @@
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
+    private NotificationAssistant mAssistant;
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
+    private RankingHandler mRankingHandler;
 
     private static class Archive {
         final int mBufferSize;
@@ -733,11 +742,12 @@
                     for (String pkgName : pkgList) {
                         if (cancelNotifications) {
                             cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
-                                    changeUserId, REASON_PACKAGE_CHANGED, null);
+                                    changeUserId, REASON_PACKAGE_CHANGED, null, null);
                         }
                     }
                 }
                 mListeners.onPackagesChanged(queryReplace, pkgList);
+                mAssistant.onPackagesChanged(queryReplace, pkgList);
                 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
                 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
             }
@@ -765,7 +775,7 @@
                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 if (userHandle >= 0) {
                     cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
-                            REASON_USER_STOPPED, null);
+                            REASON_USER_STOPPED, null, null);
                 }
             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
                 // turn off LED when user passes through lock screen
@@ -779,6 +789,7 @@
                 // Refresh managed services
                 mConditionProviders.onUserSwitched(user);
                 mListeners.onUserSwitched(user);
+                mAssistant.onUserSwitched(user);
                 mZenModeHelper.onUserSwitched(user);
             } else if (action.equals(Intent.ACTION_USER_ADDED)) {
                 mUserProfiles.updateCache(context);
@@ -874,8 +885,9 @@
             extractorNames = new String[0];
         }
         mUsageStats = new NotificationUsageStats(getContext());
+        mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
         mRankingHelper = new RankingHelper(getContext(),
-                new RankingWorkerHandler(mRankingThread.getLooper()),
+                mRankingHandler,
                 mUsageStats,
                 extractorNames);
         mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
@@ -909,6 +921,7 @@
         importOldBlockDb();
 
         mListeners = new NotificationListeners();
+        mAssistant = new NotificationAssistant();
         mStatusBar = getLocalService(StatusBarManagerInternal.class);
         mStatusBar.setNotificationDelegate(mNotificationDelegate);
 
@@ -1025,6 +1038,7 @@
             // bind to listener services.
             mSettingsObserver.observe();
             mListeners.onBootPhaseAppsCanStart();
+            mAssistant.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
         }
     }
@@ -1038,7 +1052,7 @@
         // Now, cancel any outstanding notifications that are part of a just-disabled app
         if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
-                    REASON_PACKAGE_BANNED, null);
+                    REASON_PACKAGE_BANNED, null, null);
         }
     }
 
@@ -1196,7 +1210,7 @@
             // running foreground services.
             cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
                     pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
-                    REASON_APP_CANCEL_ALL, null);
+                    REASON_APP_CANCEL_ALL, null, null);
         }
 
         @Override
@@ -1253,6 +1267,11 @@
         public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
                 int importance) {
             enforceSystemOrSystemUI("Caller not system or systemui");
+            if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
+                        UserHandle.getUserId(uid),
+                        REASON_TOPIC_BANNED, topic, null);
+            }
             mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
             savePolicyFile();
         }
@@ -1433,6 +1452,37 @@
             }
         }
 
+        /**
+         * Handle request from an approved listener to re-enable itself.
+         *
+         * @param component The componenet to be re-enabled, caller must match package.
+         */
+        @Override
+        public void requestBindListener(ComponentName component) {
+            checkCallerIsSystemOrSameApp(component.getPackageName());
+            long identity = Binder.clearCallingIdentity();
+            try {
+                ManagedServices manager = mAssistant.isComponentEnabledForCurrentProfiles(component)
+                        ? mAssistant
+                        : mListeners;
+                manager.setComponentState(component, true);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void requestUnbindListener(INotificationListener token) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                // allow bound services to disable themselves
+                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                info.getOwner().setComponentState(info.component, false);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
         @Override
         public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
             long identity = Binder.clearCallingIdentity();
@@ -1677,6 +1727,14 @@
         }
 
         @Override
+        public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
+            Preconditions.checkNotNull(owner, "Owner is null");
+            enforceSystemOrSystemUI("getRuleInstanceCount");
+
+            return mZenModeHelper.getCurrentInstanceCount(owner);
+        }
+
+        @Override
         public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
             enforcePolicyAccess(pkg, "setInterruptionFilter");
             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
@@ -1896,6 +1954,22 @@
                 Binder.restoreCallingIdentity(identity);
             }
         }
+
+        @Override
+        public void setImportanceFromAssistant(INotificationListener token, String key,
+                int importance, CharSequence explanation) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationList) {
+                    mAssistant.checkServiceTokenLocked(token);
+                    NotificationRecord n = mNotificationsByKey.get(key);
+                    n.setImportance(importance, explanation);
+                    mRankingHandler.requestSort();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     };
 
     private String disableNotificationEffects(NotificationRecord record) {
@@ -2037,6 +2111,8 @@
                     pw.print(listener.component);
                 }
                 pw.println(')');
+                pw.println("\n  Notification assistant:");
+                mAssistant.dump(pw, filter);
             }
             pw.println("\n  Policy access:");
             pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
@@ -2075,12 +2151,12 @@
         for (UserInfo user : UserManager.get(getContext()).getUsers()) {
             final int userId = user.getUserHandle().getIdentifier();
             final PackageManager packageManager = getContext().getPackageManager();
-            List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
+            List<PackageInfo> packages = packageManager.getInstalledPackagesAsUser(0, userId);
             final int packageCount = packages.size();
             for (int p = 0; p < packageCount; p++) {
                 final String packageName = packages.get(p).packageName;
                 if (filter == null || filter.matches(packageName)) {
-                    final int uid = packageManager.getPackageUid(packageName, userId);
+                    final int uid = packageManager.getPackageUidAsUser(packageName, userId);
                     if (!checkNotificationOp(packageName, uid)) {
                         packageNames.add(packageName);
                     }
@@ -2222,8 +2298,9 @@
                     mRankingHelper.extractSignals(r);
                     savePolicyFile();
 
-                    // blocked apps
-                    if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
+                    // blocked apps/topics
+                    if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
+                            || !noteNotificationOp(pkg, callingUid)) {
                         if (!isSystemNotification) {
                             Slog.e(TAG, "Suppressing notification from package " + pkg
                                     + " by user request.");
@@ -2533,9 +2610,14 @@
             updateLightsLocked();
         }
         if (buzz || beep || blink) {
-            EventLogTags.writeNotificationAlert(record.getKey(),
-                    buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
-            mHandler.post(mBuzzBeepBlinked);
+            if (((record.getSuppressedVisualEffects()
+                    & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0)) {
+                if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
+            } else {
+                EventLogTags.writeNotificationAlert(record.getKey(),
+                        buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
+                mHandler.post(mBuzzBeepBlinked);
+            }
         }
     }
 
@@ -2686,7 +2768,7 @@
         }
     }
 
-    private void handleRankingConfigChange() {
+    private void handleRankingSort() {
         synchronized (mNotificationList) {
             final int N = mNotificationList.size();
             ArrayList<String> orderBefore = new ArrayList<String>(N);
@@ -2714,7 +2796,8 @@
         record.setIntercepted(mZenModeHelper.shouldIntercept(record));
         if (record.isIntercepted()) {
             int suppressed = (mZenModeHelper.shouldSuppressLight() ? SUPPRESSED_EFFECT_LIGHTS : 0)
-                    | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0);
+                    | (mZenModeHelper.shouldSuppressPeek() ? SUPPRESSED_EFFECT_PEEK : 0)
+                    | (mZenModeHelper.shouldSuppressScreenOn() ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
             record.setSuppressedVisualEffects(suppressed);
         }
     }
@@ -2788,9 +2871,9 @@
 
     }
 
-    private final class RankingWorkerHandler extends Handler
+    private final class RankingHandlerWorker extends Handler implements RankingHandler
     {
-        public RankingWorkerHandler(Looper looper) {
+        public RankingHandlerWorker(Looper looper) {
             super(looper);
         }
 
@@ -2800,11 +2883,23 @@
                 case MESSAGE_RECONSIDER_RANKING:
                     handleRankingReconsideration(msg);
                     break;
-                case MESSAGE_RANKING_CONFIG_CHANGE:
-                    handleRankingConfigChange();
+                case MESSAGE_RANKING_SORT:
+                    handleRankingSort();
                     break;
             }
         }
+
+        public void requestSort() {
+            removeMessages(MESSAGE_RANKING_SORT);
+            sendEmptyMessage(MESSAGE_RANKING_SORT);
+        }
+
+        public void requestReconsideration(RankingReconsideration recon) {
+            Message m = Message.obtain(this,
+                    NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
+            long delay = recon.getDelay(TimeUnit.MILLISECONDS);
+            sendMessageDelayed(m, delay);
+        }
     }
 
     // Notifications
@@ -2987,11 +3082,11 @@
     }
 
     /**
-     * Cancels all notifications from a given package that have all of the
+     * Cancels all notifications from a given package or topic that have all of the
      * {@code mustHaveFlags}.
      */
     boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
-            int mustNotHaveFlags, boolean doit, int userId, int reason,
+            int mustNotHaveFlags, boolean doit, int userId, int reason, Notification.Topic topic,
             ManagedServiceInfo listener) {
         String listenerName = listener == null ? null : listener.component.toShortString();
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
@@ -3019,6 +3114,10 @@
                 if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
                     continue;
                 }
+                if (topic != null
+                        && !topic.getId().equals(r.getNotification().getTopic().getId())) {
+                    continue;
+                }
                 if (canceledNotifications == null) {
                     canceledNotifications = new ArrayList<>();
                 }
@@ -3245,7 +3344,6 @@
      * <p>Caller must hold a lock on mNotificationList.</p>
      */
     private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
-        int speedBumpIndex = -1;
         final int N = mNotificationList.size();
         ArrayList<String> keys = new ArrayList<String>(N);
         ArrayList<String> interceptedKeys = new ArrayList<String>(N);
@@ -3273,18 +3371,6 @@
                     != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
                 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
-            // Find first min-prio notification for speedbump placement.
-            if (speedBumpIndex == -1 &&
-                    // Intrusiveness trumps priority, hence ignore intrusives.
-                    !record.isRecentlyIntrusive() &&
-                    // Currently, package priority is either PRIORITY_DEFAULT or PRIORITY_MAX, so
-                    // scanning for PRIORITY_MIN within the package bucket PRIORITY_DEFAULT
-                    // (or lower as a safeguard) is sufficient to find the speedbump index.
-                    // We'll have to revisit this when more package priority buckets are introduced.
-                    record.getPackagePriority() <= Notification.PRIORITY_DEFAULT &&
-                    record.sbn.getNotification().priority == Notification.PRIORITY_MIN) {
-                speedBumpIndex = keys.size() - 1;
-            }
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -3294,7 +3380,7 @@
             importanceAr[i] = importance.get(i);
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
-                speedBumpIndex, suppressedVisualEffects, importanceAr, explanation);
+                suppressedVisualEffects, importanceAr, explanation);
     }
 
     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
@@ -3305,6 +3391,45 @@
         return true;
     }
 
+    public class NotificationAssistant extends ManagedServices {
+
+        public NotificationAssistant() {
+            super(getContext(), mHandler, mNotificationList, mUserProfiles);
+        }
+
+        @Override
+        protected Config getConfig() {
+            Config c = new Config();
+            c.caption = "notification assistant";
+            c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
+            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
+            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
+            c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
+            c.clientLabel = R.string.notification_assistant_binding_label;
+            return c;
+        }
+
+        @Override
+        protected IInterface asInterface(IBinder binder) {
+            return INotificationListener.Stub.asInterface(binder);
+        }
+
+        @Override
+        protected boolean checkType(IInterface service) {
+            return service instanceof INotificationListener;
+        }
+
+        @Override
+        protected void onServiceAdded(ManagedServiceInfo info) {
+            mListeners.registerGuestService(info);
+        }
+
+        @Override
+        protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+            mListeners.unregisterService(removed.service, removed.userid);
+        }
+    }
+
     public class NotificationListeners extends ManagedServices {
 
         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
@@ -3332,6 +3457,11 @@
         }
 
         @Override
+        protected boolean checkType(IInterface service) {
+            return service instanceof INotificationListener;
+        }
+
+        @Override
         public void onServiceAdded(ManagedServiceInfo info) {
             final INotificationListener listener = (INotificationListener) info.service;
             final NotificationRankingUpdate update;
@@ -3346,6 +3476,7 @@
             }
         }
 
+
         @Override
         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
             if (mListenersDisablingEffects.remove(removed)) {
@@ -3366,7 +3497,6 @@
 
         public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
             return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
-
         }
 
         /**
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a9f20a6..0be2edd 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -142,7 +142,7 @@
         }
         // maybe only do this for target API < N?
         if (isNoisy) {
-            if (importance == IMPORTANCE_HIGH) {
+            if (importance >= IMPORTANCE_HIGH) {
                 importance = IMPORTANCE_MAX;
             } else {
                 importance = IMPORTANCE_HIGH;
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/services/core/java/com/android/server/notification/RankingHandler.java
similarity index 77%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to services/core/java/com/android/server/notification/RankingHandler.java
index 805fe2c..80bb4f0 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.server.notification;
 
-package android.service.notification;
-
-parcelable NotificationAdjustment;
\ No newline at end of file
+public interface RankingHandler {
+    public void requestSort();
+    public void requestReconsideration(RankingReconsideration recon);
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3287f67..ce4ecd3 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -19,10 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Handler;
-import android.os.Message;
 import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -40,7 +37,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
 public class RankingHelper implements RankingConfig {
     private static final String TAG = "RankingHelper";
@@ -73,10 +69,10 @@
     private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
 
     private final Context mContext;
-    private final Handler mRankingHandler;
+    private final RankingHandler mRankingHandler;
 
-    public RankingHelper(Context context, Handler rankingHandler, NotificationUsageStats usageStats,
-            String[] extractorNames) {
+    public RankingHelper(Context context, RankingHandler rankingHandler,
+            NotificationUsageStats usageStats, String[] extractorNames) {
         mContext = context;
         mRankingHandler = rankingHandler;
 
@@ -119,10 +115,7 @@
             try {
                 RankingReconsideration recon = extractor.process(r);
                 if (recon != null) {
-                    Message m = Message.obtain(mRankingHandler,
-                            NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
-                    long delay = recon.getDelay(TimeUnit.MILLISECONDS);
-                    mRankingHandler.sendMessageDelayed(m, delay);
+                    mRankingHandler.requestReconsideration(recon);
                 }
             } catch (Throwable t) {
                 Slog.w(TAG, "NotificationSignalExtractor failed.", t);
@@ -155,7 +148,7 @@
                         if (forRestore) {
                             try {
                                 //TODO: http://b/22388012
-                                uid = pm.getPackageUid(name, UserHandle.USER_SYSTEM);
+                                uid = pm.getPackageUidAsUser(name, UserHandle.USER_SYSTEM);
                             } catch (NameNotFoundException e) {
                                 // noop
                             }
@@ -287,7 +280,7 @@
         for (int i = 0; i < N; i++) {
             mSignalExtractors[i].setConfig(this);
         }
-        mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
+        mRankingHandler.requestSort();
     }
 
     public void sort(ArrayList<NotificationRecord> notificationList) {
@@ -430,14 +423,16 @@
 
     /**
      * Sets the default importance for all new topics that appear in the future, and resets
-     * the importance of all current topics.
+     * the importance of all current topics (unless the app is being blocked).
      */
     @Override
     public void setAppImportance(String pkgName, int uid, int importance) {
         final Record r = getOrCreateRecord(pkgName, uid);
         r.importance = importance;
-        for (Topic t :  r.topics.values()) {
-            t.importance = importance;
+        if (Ranking.IMPORTANCE_NONE != importance) {
+            for (Topic t : r.topics.values()) {
+                t.importance = importance;
+            }
         }
         updateConfig();
     }
@@ -490,7 +485,9 @@
             pw.print(prefix);
             pw.println("per-package config:");
         }
+        pw.println("Records:");
         dumpRecords(pw, prefix, filter, mRecords);
+        pw.println("Restored without uid:");
         dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
     }
 
@@ -544,7 +541,7 @@
             if (r != null) {
                 try {
                     //TODO: http://b/22388012
-                    r.uid = pm.getPackageUid(r.pkg, UserHandle.USER_SYSTEM);
+                    r.uid = pm.getPackageUidAsUser(r.pkg, UserHandle.USER_SYSTEM);
                     mRestoredWithoutUids.remove(pkg);
                     mRecords.put(recordKey(r.pkg, r.uid), r);
                     updated = true;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 85c3cf8..1d91fb7 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -27,7 +27,10 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
@@ -45,7 +48,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
-import android.service.notification.IConditionListener;
+import android.service.notification.ConditionProviderService;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
@@ -91,6 +94,7 @@
     private final ZenModeConditions mConditions;
     private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
     private final Metrics mMetrics = new Metrics();
+    private final ConditionProviders.Config mServiceConfig;
 
     private int mZenMode;
     private int mUser = UserHandle.USER_SYSTEM;
@@ -113,6 +117,7 @@
         mSettingsObserver.observe();
         mFiltering = new ZenModeFiltering(mContext);
         mConditions = new ZenModeConditions(this, conditionProviders);
+        mServiceConfig = conditionProviders.getConfig();
     }
 
     public Looper getLooper() {
@@ -155,6 +160,12 @@
         }
     }
 
+    public boolean shouldSuppressScreenOn() {
+        synchronized (mConfig) {
+            return !mConfig.allowScreenOn;
+        }
+    }
+
     public void addCallback(Callback callback) {
         mCallbacks.add(callback);
     }
@@ -191,7 +202,7 @@
             config.user = user;
         }
         synchronized (mConfig) {
-            setConfig(config, "onUserSwitched");
+            setConfigLocked(config, "onUserSwitched");
         }
         cleanUpZenRules();
     }
@@ -251,22 +262,34 @@
     }
 
     public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+        if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+            throw new IllegalArgumentException("Rule already exists");
+        }
+        if (!isSystemRule(automaticZenRule)) {
+            ServiceInfo owner = getServiceInfo(automaticZenRule.getOwner());
+            if (owner == null) {
+                throw new IllegalArgumentException("Owner is not a condition provider service");
+            }
+
+            final int ruleInstanceLimit = owner.metaData.getInt(
+                    ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
+            if (ruleInstanceLimit > 0 && ruleInstanceLimit
+                    < (getCurrentInstanceCount(automaticZenRule.getOwner()) + 1)) {
+                throw new IllegalArgumentException("Rule instance limit exceeded");
+            }
+        }
+
         ZenModeConfig newConfig;
         synchronized (mConfig) {
             if (mConfig == null) return null;
             if (DEBUG) {
-                Log.d(TAG,
-                        "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" + reason);
-            }
-            if (!TextUtils.isEmpty(automaticZenRule.getId())) {
-                throw new IllegalArgumentException("Rule already exists");
+                Log.d(TAG, "addAutomaticZenRule rule= " + automaticZenRule + " reason=" + reason);
             }
             newConfig = mConfig.copy();
-
             ZenRule rule = new ZenRule();
             populateZenRule(automaticZenRule, rule, true);
             newConfig.automaticRules.put(rule.id, rule);
-            if (setConfig(newConfig, reason, true)) {
+            if (setConfigLocked(newConfig, reason, true)) {
                 return createAutomaticZenRule(rule);
             } else {
                 return null;
@@ -296,7 +319,7 @@
             }
             populateZenRule(automaticZenRule, rule, false);
             newConfig.automaticRules.put(ruleId, rule);
-            return setConfig(newConfig, reason, true);
+            return setConfigLocked(newConfig, reason, true);
         }
     }
 
@@ -314,7 +337,7 @@
                 throw new SecurityException(
                         "Cannot delete rules not owned by your condition provider");
             }
-            return setConfig(newConfig, reason, true);
+            return setConfigLocked(newConfig, reason, true);
         }
     }
 
@@ -330,10 +353,22 @@
                     newConfig.automaticRules.removeAt(i);
                 }
             }
-            return setConfig(newConfig, reason, true);
+            return setConfigLocked(newConfig, reason, true);
         }
     }
 
+    public int getCurrentInstanceCount(ComponentName owner) {
+        int count = 0;
+        synchronized (mConfig) {
+            for (ZenRule rule : mConfig.automaticRules.values()) {
+                if (rule.component != null && rule.component.equals(owner)) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
     public boolean canManageAutomaticZenRule(ZenRule rule) {
         final int callingUid = Binder.getCallingUid();
         if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
@@ -355,6 +390,29 @@
         }
     }
 
+    private boolean isSystemRule(AutomaticZenRule rule) {
+        return ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+    }
+
+    private ServiceInfo getServiceInfo(ComponentName owner) {
+        Intent queryIntent = new Intent();
+        queryIntent.setComponent(owner);
+        List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser(
+                queryIntent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+                UserHandle.getCallingUserId());
+        if (installedServices != null) {
+            for (int i = 0, count = installedServices.size(); i < count; i++) {
+                ResolveInfo resolveInfo = installedServices.get(i);
+                ServiceInfo info = resolveInfo.serviceInfo;
+                if (mServiceConfig.bindPermission.equals(info.permission)) {
+                    return info;
+                }
+            }
+        }
+        return null;
+    }
+
     private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
         if (isNew) {
             rule.id = ZenModeConfig.newRuleId();
@@ -407,7 +465,7 @@
                 newRule.conditionId = conditionId;
                 newConfig.manualRule = newRule;
             }
-            setConfig(newConfig, reason, setRingerMode);
+            setConfigLocked(newConfig, reason, setRingerMode);
         }
     }
 
@@ -435,11 +493,12 @@
             return;
         }
         pw.printf("allow(calls=%s,callsFrom=%s,repeatCallers=%s,messages=%s,messagesFrom=%s,"
-                + "events=%s,reminders=%s,lights=%s,peek=%s)\n",
+                + "events=%s,reminders=%s,lights=%s,peek=%s,screenOn=%s)\n",
                 config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
                 config.allowRepeatCallers, config.allowMessages,
                 ZenModeConfig.sourceToString(config.allowMessagesFrom),
-                config.allowEvents, config.allowReminders, config.allowLights, config.allowPeek);
+                config.allowEvents, config.allowReminders, config.allowLights, config.allowPeek,
+                config.allowScreenOn);
         pw.print(prefix); pw.print("  manualRule="); pw.println(config.manualRule);
         if (config.automaticRules.isEmpty()) return;
         final int N = config.automaticRules.size();
@@ -471,7 +530,7 @@
             }
             if (DEBUG) Log.d(TAG, "readXml");
             synchronized (mConfig) {
-                setConfig(config, "readXml");
+                setConfigLocked(config, "readXml");
             }
         }
     }
@@ -500,7 +559,7 @@
         synchronized (mConfig) {
             final ZenModeConfig newConfig = mConfig.copy();
             newConfig.applyNotificationPolicy(policy);
-            setConfig(newConfig, "setNotificationPolicy");
+            setConfigLocked(newConfig, "setNotificationPolicy");
         }
     }
 
@@ -523,7 +582,7 @@
                     }
                 }
             }
-            setConfig(newConfig, "cleanUpZenRules");
+            setConfigLocked(newConfig, "cleanUpZenRules");
         }
     }
 
@@ -536,40 +595,40 @@
         }
     }
 
-    public boolean setConfig(ZenModeConfig config, String reason) {
-        return setConfig(config, reason, true /*setRingerMode*/);
+    public boolean setConfigLocked(ZenModeConfig config, String reason) {
+        return setConfigLocked(config, reason, true /*setRingerMode*/);
     }
 
     public void setConfigAsync(ZenModeConfig config, String reason) {
         mHandler.postSetConfig(config, reason);
     }
 
-    private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+    private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
         final long identity = Binder.clearCallingIdentity();
         try {
             if (config == null || !config.isValid()) {
-                Log.w(TAG, "Invalid config in setConfig; " + config);
+                Log.w(TAG, "Invalid config in setConfigLocked; " + config);
                 return false;
             }
             if (config.user != mUser) {
                 // simply store away for background users
                 mConfigs.put(config.user, config);
-                if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+                if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
                 return true;
             }
             mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
             mConfigs.put(config.user, config);
-            if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
+            if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
             ZenLog.traceConfig(reason, mConfig, config);
             final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
                     getNotificationPolicy(config));
-            mConfig = config;
-            if (config.equals(mConfig)) {
+            if (!config.equals(mConfig)) {
                 dispatchOnConfigChanged();
             }
             if (policyChanged) {
                 dispatchOnPolicyChanged();
             }
+            mConfig = config;
             final String val = Integer.toString(config.hashCode());
             Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
             if (!evaluateZenMode(reason, setRingerMode)) {
@@ -1034,7 +1093,7 @@
                 case MSG_SET_CONFIG:
                     ConfigMessageData configData = (ConfigMessageData)msg.obj;
                     synchronized (mConfig) {
-                        setConfig(configData.config, configData.reason);
+                        setConfigLocked(configData.config, configData.reason);
                     }
                     break;
             }
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index b254f29..6c338c1 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -261,6 +261,7 @@
                     && doesPackageSupportRuntimePermissions(setupPackage)) {
                 grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
                 grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId);
+                grantRuntimePermissionsLPw(setupPackage, LOCATION_PERMISSIONS, userId);
             }
 
             // Camera
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 99a051a..508bf91 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,17 +16,21 @@
 
 package com.android.server.pm;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageStats;
 import android.os.Build;
-import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.os.InstallerConnection;
+import com.android.internal.os.InstallerConnection.InstallerException;
+import com.android.server.SystemService;
+
 import dalvik.system.VMRuntime;
 
-import com.android.internal.os.InstallerConnection;
-import com.android.server.SystemService;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
@@ -46,6 +50,20 @@
     /** Run the application with the JIT compiler */
     public static final int DEXOPT_USEJIT       = 1 << 5;
 
+    /** @hide */
+    @IntDef(flag = true, value = {
+            FLAG_DE_STORAGE,
+            FLAG_CE_STORAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StorageFlags {}
+
+    public static final int FLAG_DE_STORAGE = 1 << 0;
+    public static final int FLAG_CE_STORAGE = 1 << 1;
+
+    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2;
+    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3;
+
     private final InstallerConnection mInstaller;
 
     public Installer(Context context) {
@@ -67,423 +85,137 @@
         mInstaller.waitForConnection();
     }
 
-    private static String escapeNull(String arg) {
-        if (TextUtils.isEmpty(arg)) {
-            return "!";
-        } else {
-            if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
-                throw new IllegalArgumentException(arg);
-            }
-            return arg;
-        }
+    public void createAppData(String uuid, String pkgname, int userid, int flags, int appid,
+            String seinfo) throws InstallerException {
+        mInstaller.execute("create_app_data", uuid, pkgname, userid, flags, appid, seinfo);
     }
 
-    @Deprecated
-    public int install(String name, int uid, int gid, String seinfo) {
-        return install(null, name, uid, gid, seinfo);
+    public void restoreconAppData(String uuid, String pkgname, int userid, int flags, int appid,
+            String seinfo) throws InstallerException {
+        mInstaller.execute("restorecon_app_data", uuid, pkgname, userid, flags, appid,
+                seinfo);
     }
 
-    public int install(String uuid, String name, int uid, int gid, String seinfo) {
-        StringBuilder builder = new StringBuilder("install");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(gid);
-        builder.append(' ');
-        builder.append(seinfo != null ? seinfo : "!");
-        return mInstaller.execute(builder.toString());
+    public void clearAppData(String uuid, String pkgname, int userid, int flags)
+            throws InstallerException {
+        mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags);
     }
 
-    public int dexopt(String apkPath, int uid, String instructionSet,
-            int dexoptNeeded, int dexFlags) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
+    public void destroyAppData(String uuid, String pkgname, int userid, int flags)
+            throws InstallerException {
+        mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags);
     }
 
-    public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, @Nullable String outputPath, int dexFlags) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-        return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
-                outputPath, dexFlags);
+    public void moveCompleteApp(String from_uuid, String to_uuid, String package_name,
+            String data_app_name, int appid, String seinfo) throws InstallerException {
+        mInstaller.execute("move_complete_app", from_uuid, to_uuid, package_name,
+                data_app_name, appid, seinfo);
     }
 
-    public int idmap(String targetApkPath, String overlayApkPath, int uid) {
-        StringBuilder builder = new StringBuilder("idmap");
-        builder.append(' ');
-        builder.append(targetApkPath);
-        builder.append(' ');
-        builder.append(overlayApkPath);
-        builder.append(' ');
-        builder.append(uid);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int movedex(String srcPath, String dstPath, String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("movedex");
-        builder.append(' ');
-        builder.append(srcPath);
-        builder.append(' ');
-        builder.append(dstPath);
-        builder.append(' ');
-        builder.append(instructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int rmdex(String codePath, String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("rmdex");
-        builder.append(' ');
-        builder.append(codePath);
-        builder.append(' ');
-        builder.append(instructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-    /**
-     * Removes packageDir or its subdirectory
-     */
-    public int rmPackageDir(String packageDir) {
-        StringBuilder builder = new StringBuilder("rmpackagedir");
-        builder.append(' ');
-        builder.append(packageDir);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int remove(String name, int userId) {
-        return remove(null, name, userId);
-    }
-
-    public int remove(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("remove");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int fixUid(String name, int uid, int gid) {
-        return fixUid(null, name, uid, gid);
-    }
-
-    public int fixUid(String uuid, String name, int uid, int gid) {
-        StringBuilder builder = new StringBuilder("fixuid");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(gid);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int deleteCacheFiles(String name, int userId) {
-        return deleteCacheFiles(null, name, userId);
-    }
-
-    public int deleteCacheFiles(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("rmcache");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int deleteCodeCacheFiles(String name, int userId) {
-        return deleteCodeCacheFiles(null, name, userId);
-    }
-
-    public int deleteCodeCacheFiles(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("rmcodecache");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int createUserData(String name, int uid, int userId, String seinfo) {
-        return createUserData(null, name, uid, userId, seinfo);
-    }
-
-    public int createUserData(String uuid, String name, int uid, int userId, String seinfo) {
-        StringBuilder builder = new StringBuilder("mkuserdata");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(' ');
-        builder.append(userId);
-        builder.append(' ');
-        builder.append(seinfo != null ? seinfo : "!");
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int createUserConfig(int userId) {
-        StringBuilder builder = new StringBuilder("mkuserconfig");
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int removeUserDataDirs(int userId) {
-        return removeUserDataDirs(null, userId);
-    }
-
-    public int removeUserDataDirs(String uuid, int userId) {
-        StringBuilder builder = new StringBuilder("rmuser");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int copyCompleteApp(String fromUuid, String toUuid, String packageName,
-            String dataAppName, int appId, String seinfo) {
-        StringBuilder builder = new StringBuilder("cpcompleteapp");
-        builder.append(' ');
-        builder.append(escapeNull(fromUuid));
-        builder.append(' ');
-        builder.append(escapeNull(toUuid));
-        builder.append(' ');
-        builder.append(packageName);
-        builder.append(' ');
-        builder.append(dataAppName);
-        builder.append(' ');
-        builder.append(appId);
-        builder.append(' ');
-        builder.append(seinfo);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int clearUserData(String name, int userId) {
-        return clearUserData(null, name, userId);
-    }
-
-    public int clearUserData(String uuid, String name, int userId) {
-        StringBuilder builder = new StringBuilder("rmuserdata");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(name);
-        builder.append(' ');
-        builder.append(userId);
-        return mInstaller.execute(builder.toString());
-    }
-
-    public int markBootComplete(String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("markbootcomplete");
-        builder.append(' ');
-        builder.append(instructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int freeCache(long freeStorageSize) {
-        return freeCache(null, freeStorageSize);
-    }
-
-    public int freeCache(String uuid, long freeStorageSize) {
-        StringBuilder builder = new StringBuilder("freecache");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(String.valueOf(freeStorageSize));
-        return mInstaller.execute(builder.toString());
-    }
-
-    @Deprecated
-    public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
-            String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
-        return getSizeInfo(null, pkgName, persona, apkPath, libDirPath, fwdLockApkPath, asecPath,
-                instructionSets, pStats);
-    }
-
-    public int getSizeInfo(String uuid, String pkgName, int persona, String apkPath,
+    public void getAppSize(String uuid, String pkgname, int userid, int flags, String apkPath,
             String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets,
-            PackageStats pStats) {
+            PackageStats pStats) throws InstallerException {
         for (String instructionSet : instructionSets) {
-            if (!isValidInstructionSet(instructionSet)) {
-                Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-                return -1;
-            }
+            assertValidInstructionSet(instructionSet);
         }
 
-        StringBuilder builder = new StringBuilder("getsize");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(persona);
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
         // TODO: Extend getSizeInfo to look at the full subdirectory tree,
         // not just the first level.
-        builder.append(libDirPath != null ? libDirPath : "!");
-        builder.append(' ');
-        builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
-        builder.append(' ');
-        builder.append(asecPath != null ? asecPath : "!");
-        builder.append(' ');
         // TODO: Extend getSizeInfo to look at *all* instrution sets, not
         // just the primary.
-        builder.append(instructionSets[0]);
-
-        String s = mInstaller.transact(builder.toString());
-        String res[] = s.split(" ");
+        final String rawRes = mInstaller.executeForResult("get_app_size", uuid, pkgname, userid,
+                flags, apkPath, libDirPath, fwdLockApkPath, asecPath, instructionSets[0]);
+        final String res[] = rawRes.split(" ");
 
         if ((res == null) || (res.length != 5)) {
-            return -1;
+            throw new InstallerException("Invalid size result: " + rawRes);
         }
         try {
             pStats.codeSize = Long.parseLong(res[1]);
             pStats.dataSize = Long.parseLong(res[2]);
             pStats.cacheSize = Long.parseLong(res[3]);
             pStats.externalCodeSize = Long.parseLong(res[4]);
-            return Integer.parseInt(res[0]);
         } catch (NumberFormatException e) {
-            return -1;
+            throw new InstallerException("Invalid size result: " + rawRes);
         }
     }
 
-    public int moveFiles() {
-        return mInstaller.execute("movefiles");
+    public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
+            int dexFlags) throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
     }
 
-    @Deprecated
-    public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) {
-        return linkNativeLibraryDirectory(null, dataPath, nativeLibPath32, userId);
+    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+            int dexoptNeeded, @Nullable String outputPath, int dexFlags)
+                    throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
+                outputPath, dexFlags);
+    }
+
+    public void idmap(String targetApkPath, String overlayApkPath, int uid)
+            throws InstallerException {
+        mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid);
+    }
+
+    public void rmdex(String codePath, String instructionSet) throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.execute("rmdex", codePath, instructionSet);
+    }
+
+    public void rmPackageDir(String packageDir) throws InstallerException {
+        mInstaller.execute("rmpackagedir", packageDir);
+    }
+
+    public void createUserConfig(int userid) throws InstallerException {
+        mInstaller.execute("mkuserconfig", userid);
+    }
+
+    public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
+        mInstaller.execute("rmuser", uuid, userid);
+    }
+
+    public void markBootComplete(String instructionSet) throws InstallerException {
+        assertValidInstructionSet(instructionSet);
+        mInstaller.execute("markbootcomplete", instructionSet);
+    }
+
+    public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
+        mInstaller.execute("freecache", uuid, freeStorageSize);
+    }
+
+    public void moveFiles() throws InstallerException {
+        mInstaller.execute("movefiles");
     }
 
     /**
-     * Links the 32 bit native library directory in an application's data directory to the
-     * real location for backward compatibility. Note that no such symlink is created for
-     * 64 bit shared libraries.
-     *
-     * @return -1 on error
+     * Links the 32 bit native library directory in an application's data
+     * directory to the real location for backward compatibility. Note that no
+     * such symlink is created for 64 bit shared libraries.
      */
-    public int linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
-            int userId) {
-        if (dataPath == null) {
-            Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
-            return -1;
-        } else if (nativeLibPath32 == null) {
-            Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
-            return -1;
-        }
-
-        StringBuilder builder = new StringBuilder("linklib");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(dataPath);
-        builder.append(' ');
-        builder.append(nativeLibPath32);
-        builder.append(' ');
-        builder.append(userId);
-
-        return mInstaller.execute(builder.toString());
+    public void linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
+            int userId) throws InstallerException {
+        mInstaller.execute("linklib", uuid, dataPath, nativeLibPath32, userId);
     }
 
-    @Deprecated
-    public boolean restoreconData(String pkgName, String seinfo, int uid) {
-        return restoreconData(null, pkgName, seinfo, uid);
+    public void createOatDir(String oatDir, String dexInstructionSet)
+            throws InstallerException {
+        mInstaller.execute("createoatdir", oatDir, dexInstructionSet);
     }
 
-    public boolean restoreconData(String uuid, String pkgName, String seinfo, int uid) {
-        StringBuilder builder = new StringBuilder("restorecondata");
-        builder.append(' ');
-        builder.append(escapeNull(uuid));
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(seinfo != null ? seinfo : "!");
-        builder.append(' ');
-        builder.append(uid);
-        return (mInstaller.execute(builder.toString()) == 0);
+    public void linkFile(String relativePath, String fromBase, String toBase)
+            throws InstallerException {
+        mInstaller.execute("linkfile", relativePath, fromBase, toBase);
     }
 
-    public int createOatDir(String oatDir, String dexInstructionSet) {
-        StringBuilder builder = new StringBuilder("createoatdir");
-        builder.append(' ');
-        builder.append(oatDir);
-        builder.append(' ');
-        builder.append(dexInstructionSet);
-        return mInstaller.execute(builder.toString());
-    }
-
-
-    public int linkFile(String relativePath, String fromBase, String toBase) {
-        StringBuilder builder = new StringBuilder("linkfile");
-        builder.append(' ');
-        builder.append(relativePath);
-        builder.append(' ');
-        builder.append(fromBase);
-        builder.append(' ');
-        builder.append(toBase);
-        return mInstaller.execute(builder.toString());
-    }
-
-    /**
-     * Returns true iff. {@code instructionSet} is a valid instruction set.
-     */
-    private static boolean isValidInstructionSet(String instructionSet) {
-        if (instructionSet == null) {
-            return false;
-        }
-
+    private static void assertValidInstructionSet(String instructionSet)
+            throws InstallerException {
         for (String abi : Build.SUPPORTED_ABIS) {
-            if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
-                return true;
+            if (VMRuntime.getInstructionSet(abi).equals(instructionSet)) {
+                return;
             }
         }
-
-        return false;
+        throw new InstallerException("Invalid instruction set: " + instructionSet);
     }
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 0796811..18618d5 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -24,8 +24,8 @@
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
@@ -45,7 +45,6 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.server.SystemService;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -200,7 +199,8 @@
             mainIntent.setPackage(packageName);
             long ident = Binder.clearCallingIdentity();
             try {
-                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */,
+                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                         user.getIdentifier());
                 return new ParceledListSlice<>(apps);
             } finally {
@@ -218,7 +218,8 @@
 
             long ident = Binder.clearCallingIdentity();
             try {
-                ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
+                ResolveInfo app = mPm.resolveActivityAsUser(intent,
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
                 return app;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -236,7 +237,8 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
-                PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
+                PackageInfo info = pm.getPackageInfo(packageName,
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
                 return info != null && info.applicationInfo.enabled;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -254,7 +256,8 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
-                ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+                ActivityInfo info = pm.getActivityInfo(component,
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
                 return info != null;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -279,7 +282,8 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 IPackageManager pm = AppGlobals.getPackageManager();
-                ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+                ActivityInfo info = pm.getActivityInfo(component,
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
                 if (!info.exported) {
                     throw new SecurityException("Cannot launch non-exported components "
                             + component);
@@ -289,7 +293,7 @@
                 // as calling startActivityAsUser ignores the category and just
                 // resolves based on the component if present.
                 List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
-                        0 /* flags */, user.getIdentifier());
+                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING, user.getIdentifier());
                 final int size = apps.size();
                 for (int i = 0; i < size; ++i) {
                     ActivityInfo activityInfo = apps.get(i).activityInfo;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index d29a623..b45a922 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -27,6 +27,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.InstallerConnection.InstallerException;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -166,12 +168,13 @@
                             | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
                             | (debuggable ? DEXOPT_DEBUGGABLE : 0)
                             | DEXOPT_BOOTCOMPLETE;
-                    final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
-                            pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
-
-                    // Dex2oat might fail due to compiler / verifier errors.
-                    if (ret == 0) {
+                    try {
+                        mPackageManagerService.mInstaller.dexopt(path, sharedGid,
+                                pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir,
+                                dexFlags);
                         performedDexOpt = true;
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to dexopt", e);
                     }
                 }
             }
@@ -210,8 +213,13 @@
         File codePath = new File(pkg.codePath);
         if (codePath.isDirectory()) {
             File oatDir = getOatDir(codePath);
-            mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
-                    dexInstructionSet);
+            try {
+                mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
+                        dexInstructionSet);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to create oat dir", e);
+                return null;
+            }
             return oatDir.getAbsolutePath();
         }
         return null;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 7e4e46b..23a58d0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -258,11 +258,7 @@
         for (File stage : unclaimedStages) {
             Slog.w(TAG, "Deleting orphan stage " + stage);
             synchronized (mPm.mInstallLock) {
-                if (stage.isDirectory()) {
-                    mPm.mInstaller.rmPackageDir(stage.getAbsolutePath());
-                } else {
-                    stage.delete();
-                }
+                mPm.removeCodePathLI(stage);
             }
         }
     }
@@ -386,8 +382,8 @@
         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
         final int userId = readIntAttribute(in, ATTR_USER_ID);
         final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
-        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID,
-                mPm.getPackageUid(installerPackageName, userId));
+        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, mPm.getPackageUid(
+                installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
         final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
         final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
         final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 66d10b5..b84ffa3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -69,6 +69,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -832,9 +833,14 @@
         throw new IOException("File: " + pathStr + " outside base: " + baseStr);
     }
 
-    private void createOatDirs(List<String> instructionSets, File fromDir) {
+    private void createOatDirs(List<String> instructionSets, File fromDir)
+            throws PackageManagerException {
         for (String instructionSet : instructionSets) {
-            mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+            try {
+                mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+            } catch (InstallerException e) {
+                throw PackageManagerException.from(e);
+            }
         }
     }
 
@@ -842,13 +848,12 @@
             throws IOException {
         for (File fromFile : fromFiles) {
             final String relativePath = getRelativePath(fromFile, fromDir);
-            final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
-                    toDir.getAbsolutePath());
-
-            if (ret < 0) {
-                // installd will log failure details.
+            try {
+                mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
+                        toDir.getAbsolutePath());
+            } catch (InstallerException e) {
                 throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
-                        + fromDir + ", " + toDir + ")");
+                        + fromDir + ", " + toDir + ")", e);
             }
         }
 
@@ -947,7 +952,7 @@
         }
 
         final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
-                UserHandle.USER_SYSTEM);
+                PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
         final int gid = UserHandle.getSharedAppGid(uid);
         if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
@@ -1041,7 +1046,10 @@
             }
         }
         if (stageDir != null) {
-            mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            try {
+                mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            } catch (InstallerException ignored) {
+            }
         }
         if (stageCid != null) {
             PackageHelper.destroySdDir(stageCid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index a41636e..f243e63 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -16,12 +16,20 @@
 
 package com.android.server.pm;
 
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.PackageParserException;
 
+import com.android.internal.os.InstallerConnection.InstallerException;
+
 /** {@hide} */
 public class PackageManagerException extends Exception {
     public final int error;
 
+    public PackageManagerException(String detailMessage) {
+        super(detailMessage);
+        this.error = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+    }
+
     public PackageManagerException(int error, String detailMessage) {
         super(detailMessage);
         this.error = error;
@@ -36,4 +44,10 @@
             throws PackageManagerException {
         throw new PackageManagerException(e.error, e.getMessage(), e.getCause());
     }
+
+    public static PackageManagerException from(InstallerException e)
+            throws PackageManagerException {
+        throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+                e.getMessage(), e.getCause());
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7bb6d1d..6277310 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -46,7 +46,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
@@ -59,6 +58,11 @@
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
 import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
@@ -71,7 +75,6 @@
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
-
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
@@ -88,6 +91,8 @@
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
@@ -106,6 +111,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AppsQueryHelper;
+import android.content.pm.ComponentInfo;
 import android.content.pm.EphemeralApplicationInfo;
 import android.content.pm.EphemeralResolveInfo;
 import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
@@ -122,7 +128,6 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
-import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
@@ -185,7 +190,6 @@
 import android.security.SystemKeyStore;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.StructStat;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -207,12 +211,12 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -220,6 +224,7 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.IntentResolver;
@@ -227,6 +232,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
+import com.android.server.pm.Installer.StorageFlags;
 import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
@@ -308,10 +314,13 @@
     private static final boolean DEBUG_DEXOPT = false;
     private static final boolean DEBUG_ABI_SELECTION = false;
     private static final boolean DEBUG_EPHEMERAL = false;
-    private static final boolean DEBUG_ENCRYPTION_AWARE = false;
+    private static final boolean DEBUG_TRIAGED_MISSING = false;
+    private static final boolean DEBUG_APP_DATA = false;
 
     static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
 
+    private static final boolean DISABLE_EPHEMERAL_APPS = true;
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -509,9 +518,6 @@
     // If mac_permissions.xml was found for seinfo labeling.
     boolean mFoundPolicyFile;
 
-    // If a recursive restorecon of /data/data/<pkg> is needed.
-    private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
-
     private final EphemeralApplicationRegistry mEphemeralApplicationRegistry;
 
     public static final class SharedLibraryEntry {
@@ -597,7 +603,9 @@
 
     boolean mResolverReplaced = false;
 
-    private final ComponentName mIntentFilterVerifierComponent;
+    private final @Nullable ComponentName mIntentFilterVerifierComponent;
+    private final @Nullable IntentFilterVerifier<ActivityIntentInfo> mIntentFilterVerifier;
+
     private int mIntentFilterVerificationToken = 0;
 
     /** Component that knows whether or not an ephemeral application exists */
@@ -808,7 +816,7 @@
                         packageName);
             }
             if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "Adding verification filter for " + packageName + " : " + filter);
+                Slog.d(TAG, "Adding verification filter for " + packageName + ": " + filter);
             }
             ivs.addFilter(filter);
             return true;
@@ -833,8 +841,6 @@
                         filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
     }
 
-    private IntentFilterVerifier mIntentFilterVerifier;
-
     // Set of pending broadcasts for aggregating enable/disable of components.
     static class PendingPackageBroadcasts {
         // for each user id, a map of <package name -> components within that package>
@@ -969,8 +975,32 @@
     private static final String TAG_DEFAULT_APPS = "da";
     private static final String TAG_INTENT_FILTER_VERIFICATION = "iv";
 
-    final String mRequiredVerifierPackage;
-    final String mRequiredInstallerPackage;
+    private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup";
+    private static final String TAG_ALL_GRANTS = "rt-grants";
+    private static final String TAG_GRANT = "grant";
+    private static final String ATTR_PACKAGE_NAME = "pkg";
+
+    private static final String TAG_PERMISSION = "perm";
+    private static final String ATTR_PERMISSION_NAME = "name";
+    private static final String ATTR_IS_GRANTED = "g";
+    private static final String ATTR_USER_SET = "set";
+    private static final String ATTR_USER_FIXED = "fixed";
+    private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
+
+    // System/policy permission grants are not backed up
+    private static final int SYSTEM_RUNTIME_GRANT_MASK =
+            FLAG_PERMISSION_POLICY_FIXED
+            | FLAG_PERMISSION_SYSTEM_FIXED
+            | FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+
+    // And we back up these user-adjusted states
+    private static final int USER_RUNTIME_GRANT_MASK =
+            FLAG_PERMISSION_USER_SET
+            | FLAG_PERMISSION_USER_FIXED
+            | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+
+    final @Nullable String mRequiredVerifierPackage;
+    final @Nullable String mRequiredInstallerPackage;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
 
@@ -1481,16 +1511,25 @@
                                 deleteOld = true;
                             }
 
-                            // If this app is a browser and it's newly-installed for some
-                            // users, clear any default-browser state in those users
+
+                            // Work that needs to happen on first install within each user
                             if (firstUsers.length > 0) {
-                                // the app's nature doesn't depend on the user, so we can just
-                                // check its browser nature in any user and generalize.
-                                if (packageIsBrowser(packageName, firstUsers[0])) {
+                                for (int userId : firstUsers) {
                                     synchronized (mPackages) {
-                                        for (int userId : firstUsers) {
-                                            mSettings.setDefaultBrowserPackageNameLPw(null, userId);
+                                        // If this app is a browser and it's newly-installed for
+                                        // some users, clear any default-browser state in those
+                                        // users.  The app's nature doesn't depend on the user,
+                                        // so we can just check its browser nature in any user
+                                        // and generalize.
+                                        if (packageIsBrowser(packageName, firstUsers[0])) {
+                                            mSettings.setDefaultBrowserPackageNameLPw(
+                                                    null, userId);
                                         }
+
+                                        // We may also need to apply pending (restored) runtime
+                                        // permission grants within these users.
+                                        mSettings.applyPendingPermissionGrantsLPw(
+                                                packageName, userId);
                                     }
                                 }
                             }
@@ -1741,7 +1780,7 @@
         @Override
         public void onVolumeForgotten(String fsUuid) {
             if (TextUtils.isEmpty(fsUuid)) {
-                Slog.w(TAG, "Forgetting internal storage is probably a mistake; ignoring");
+                Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring");
                 return;
             }
 
@@ -2059,7 +2098,7 @@
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
-                        } catch (IOException e) {
+                        } catch (IOException | InstallerException e) {
                             Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
                                     + e.getMessage());
                         }
@@ -2128,7 +2167,11 @@
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
 
             if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
-            mInstaller.moveFiles();
+            try {
+                mInstaller.moveFiles();
+            } catch (InstallerException e) {
+                logCriticalInfo(Log.WARN, "Update commands failed: " + e);
+            }
 
             // Prune any system packages that no longer exist.
             final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
@@ -2329,6 +2372,17 @@
                 }
             }
 
+            // Prepare storage for system user really early during boot,
+            // since core system apps like SettingsProvider and SystemUI
+            // can't wait for user to start
+            final int flags;
+            if (StorageManager.isFileBasedEncryptionEnabled()) {
+                flags = Installer.FLAG_DE_STORAGE;
+            } else {
+                flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+            }
+            reconcileAppsData(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM, flags);
+
             // If this is first boot after an OTA, and a normal boot, then
             // we need to clear code cache directories.
             if (mIsUpgrade && !onlyCore) {
@@ -2357,15 +2411,21 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                     SystemClock.uptimeMillis());
 
-            mRequiredVerifierPackage = getRequiredVerifierLPr();
-            mRequiredInstallerPackage = getRequiredInstallerLPr();
+            if (!mOnlyCore) {
+                mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
+                mRequiredInstallerPackage = getRequiredInstallerLPr();
+                mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
+                mIntentFilterVerifier = new IntentVerifierProxy(mContext,
+                        mIntentFilterVerifierComponent);
+            } else {
+                mRequiredVerifierPackage = null;
+                mRequiredInstallerPackage = null;
+                mIntentFilterVerifierComponent = null;
+                mIntentFilterVerifier = null;
+            }
 
             mInstallerService = new PackageInstallerService(context, this);
 
-            mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
-            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
-                    mIntentFilterVerifierComponent);
-
             final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
             final ComponentName ephemeralInstallerComponent = getEphemeralInstallerLPr();
             // both the installer and resolver must be present to enable ephemeral
@@ -2427,113 +2487,61 @@
         return mIsUpgrade;
     }
 
-    private String getRequiredVerifierLPr() {
-        final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
-        // We only care about verifier that's installed under system user.
-        final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
-                PackageManager.GET_DISABLED_COMPONENTS, UserHandle.USER_SYSTEM);
+    private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
+        final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
 
-        String requiredVerifier = null;
-
-        final int N = receivers.size();
-        for (int i = 0; i < N; i++) {
-            final ResolveInfo info = receivers.get(i);
-
-            if (info.activityInfo == null) {
-                continue;
-            }
-
-            final String packageName = info.activityInfo.packageName;
-
-            if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
-                    packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
-                continue;
-            }
-
-            if (requiredVerifier != null) {
-                throw new RuntimeException("There can be only one required verifier");
-            }
-
-            requiredVerifier = packageName;
+        final List<ResolveInfo> matches = queryIntentReceivers(intent, PACKAGE_MIME_TYPE,
+                MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
+        if (matches.size() == 1) {
+            return matches.get(0).getComponentInfo().packageName;
+        } else {
+            Log.e(TAG, "There should probably be exactly one verifier; found " + matches);
+            return null;
         }
-
-        return requiredVerifier;
     }
 
-    private String getRequiredInstallerLPr() {
-        Intent installerIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
-        installerIntent.addCategory(Intent.CATEGORY_DEFAULT);
-        installerIntent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+    private @NonNull String getRequiredInstallerLPr() {
+        final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
 
-        final List<ResolveInfo> installers = queryIntentActivities(installerIntent,
-                PACKAGE_MIME_TYPE, 0, UserHandle.USER_SYSTEM);
-
-        String requiredInstaller = null;
-
-        final int N = installers.size();
-        for (int i = 0; i < N; i++) {
-            final ResolveInfo info = installers.get(i);
-            final String packageName = info.activityInfo.packageName;
-
-            if (!info.activityInfo.applicationInfo.isSystemApp()) {
-                continue;
-            }
-
-            if (requiredInstaller != null) {
-                throw new RuntimeException("There must be one required installer");
-            }
-
-            requiredInstaller = packageName;
+        final List<ResolveInfo> matches = queryIntentActivities(intent, PACKAGE_MIME_TYPE,
+                MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
+        if (matches.size() == 1) {
+            return matches.get(0).getComponentInfo().packageName;
+        } else {
+            throw new RuntimeException("There must be exactly one installer; found " + matches);
         }
-
-        if (requiredInstaller == null) {
-            throw new RuntimeException("There must be one required installer");
-        }
-
-        return requiredInstaller;
     }
 
-    private ComponentName getIntentFilterVerifierComponentNameLPr() {
-        final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
-        final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
-                PackageManager.GET_DISABLED_COMPONENTS, UserHandle.USER_SYSTEM);
+    private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
+        final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
 
-        ComponentName verifierComponentName = null;
-
-        int priority = -1000;
-        final int N = receivers.size();
+        final List<ResolveInfo> matches = queryIntentReceivers(intent, PACKAGE_MIME_TYPE,
+                MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
+        ResolveInfo best = null;
+        final int N = matches.size();
         for (int i = 0; i < N; i++) {
-            final ResolveInfo info = receivers.get(i);
-
-            if (info.activityInfo == null) {
-                continue;
-            }
-
-            final String packageName = info.activityInfo.packageName;
-
-            final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                continue;
-            }
-
+            final ResolveInfo cur = matches.get(i);
+            final String packageName = cur.getComponentInfo().packageName;
             if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
                     packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
                 continue;
             }
 
-            // Select the IntentFilterVerifier with the highest priority
-            if (priority < info.priority) {
-                priority = info.priority;
-                verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
-                if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Selecting IntentFilterVerifier: "
-                        + verifierComponentName + " with priority: " + info.priority);
+            if (best == null || cur.priority > best.priority) {
+                best = cur;
             }
         }
 
-        return verifierComponentName;
+        if (best != null) {
+            return best.getComponentInfo().getComponentName();
+        } else {
+            throw new RuntimeException("There must be at least one intent filter verifier");
+        }
     }
 
-    private ComponentName getEphemeralResolverLPr() {
+    private @Nullable ComponentName getEphemeralResolverLPr() {
         final String[] packageArray =
                 mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
         if (packageArray.length == 0) {
@@ -2543,9 +2551,9 @@
             return null;
         }
 
-        Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
-        final List<ResolveInfo> resolvers = queryIntentServices(resolverIntent,
-                null /*resolvedType*/, 0 /*flags*/, UserHandle.USER_SYSTEM);
+        final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
+        final List<ResolveInfo> resolvers = queryIntentServices(resolverIntent, null,
+                MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
 
         final int N = resolvers.size();
         if (N == 0) {
@@ -2584,36 +2592,21 @@
         return null;
     }
 
-    private ComponentName getEphemeralInstallerLPr() {
-        Intent installerIntent = new Intent(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
-        installerIntent.addCategory(Intent.CATEGORY_DEFAULT);
-        installerIntent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
-        final List<ResolveInfo> installers = queryIntentActivities(installerIntent,
-                PACKAGE_MIME_TYPE, 0 /*flags*/, 0 /*userId*/);
+    private @Nullable ComponentName getEphemeralInstallerLPr() {
+        final Intent intent = new Intent(Intent.ACTION_INSTALL_EPHEMERAL_PACKAGE);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
 
-        ComponentName ephemeralInstaller = null;
-
-        final int N = installers.size();
-        for (int i = 0; i < N; i++) {
-            final ResolveInfo info = installers.get(i);
-            final String packageName = info.activityInfo.packageName;
-
-            if (!info.activityInfo.applicationInfo.isSystemApp()) {
-                if (DEBUG_EPHEMERAL) {
-                    Slog.d(TAG, "Ephemeral installer is not system app;"
-                            + " pkg: " + packageName + ", info:" + info);
-                }
-                continue;
-            }
-
-            if (ephemeralInstaller != null) {
-                throw new RuntimeException("There must only be one ephemeral installer");
-            }
-
-            ephemeralInstaller = new ComponentName(packageName, info.activityInfo.name);
+        final List<ResolveInfo> matches = queryIntentActivities(intent, PACKAGE_MIME_TYPE,
+                MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
+        if (matches.size() == 0) {
+            return null;
+        } else if (matches.size() == 1) {
+            return matches.get(0).getComponentInfo().getComponentName();
+        } else {
+            throw new RuntimeException(
+                    "There must be at most one ephemeral installer; found " + matches);
         }
-
-        return ephemeralInstaller;
     }
 
     private void primeDomainVerificationsLPw(int userId) {
@@ -2660,7 +2653,7 @@
                             + "' does not handle web links");
                 }
             } else {
-                Slog.w(TAG, "Unknown package '" + packageName + "' in sysconfig <app-link>");
+                Slog.w(TAG, "Unknown package " + packageName + " in sysconfig <app-link>");
             }
         }
 
@@ -2763,11 +2756,7 @@
 
         removeDataDirsLI(ps.volumeUuid, ps.name);
         if (ps.codePath != null) {
-            if (ps.codePath.isDirectory()) {
-                mInstaller.rmPackageDir(ps.codePath.getAbsolutePath());
-            } else {
-                ps.codePath.delete();
-            }
+            removeCodePathLI(ps.codePath);
         }
         if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
             if (ps.resourcePath.isDirectory()) {
@@ -2848,6 +2837,7 @@
     @Override
     public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
+        flags = updateFlagsForPackage(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package info");
         // reader
         synchronized (mPackages) {
@@ -2857,7 +2847,7 @@
             if (p != null) {
                 return generatePackageInfo(p, flags, userId);
             }
-            if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+            if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
                 return generatePackageInfoFromSettingsLPw(packageName, flags, userId);
             }
         }
@@ -2891,24 +2881,20 @@
     }
 
     @Override
-    public int getPackageUid(String packageName, int userId) {
-        return getPackageUidEtc(packageName, 0, userId);
-    }
-
-    @Override
-    public int getPackageUidEtc(String packageName, int flags, int userId) {
+    public int getPackageUid(String packageName, int flags, int userId) {
         if (!sUserManager.exists(userId)) return -1;
+        flags = updateFlagsForPackage(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
 
         // reader
         synchronized (mPackages) {
             final PackageParser.Package p = mPackages.get(packageName);
-            if (p != null) {
+            if (p != null && p.isMatch(flags)) {
                 return UserHandle.getUid(userId, p.applicationInfo.uid);
             }
-            if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+            if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
-                if (ps != null) {
+                if (ps != null && ps.isMatch(flags)) {
                     return UserHandle.getUid(userId, ps.appId);
                 }
             }
@@ -2918,29 +2904,22 @@
     }
 
     @Override
-    public int[] getPackageGids(String packageName, int userId) {
-        return getPackageGidsEtc(packageName, 0, userId);
-    }
-
-    @Override
-    public int[] getPackageGidsEtc(String packageName, int flags, int userId) {
-        if (!sUserManager.exists(userId)) {
-            return null;
-        }
-
+    public int[] getPackageGids(String packageName, int flags, int userId) {
+        if (!sUserManager.exists(userId)) return null;
+        flags = updateFlagsForPackage(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
                 "getPackageGids");
 
         // reader
         synchronized (mPackages) {
             final PackageParser.Package p = mPackages.get(packageName);
-            if (p != null) {
+            if (p != null && p.isMatch(flags)) {
                 PackageSetting ps = (PackageSetting) p.mExtras;
                 return ps.getPermissionsState().computeGids(userId);
             }
-            if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+            if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
-                if (ps != null) {
+                if (ps != null && ps.isMatch(flags)) {
                     return ps.getPermissionsState().computeGids(userId);
                 }
             }
@@ -2949,8 +2928,7 @@
         return null;
     }
 
-    static PermissionInfo generatePermissionInfo(
-            BasePermission bp, int flags) {
+    static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) {
         if (bp.perm != null) {
             return PackageParser.generatePermissionInfo(bp.perm, flags);
         }
@@ -3047,7 +3025,7 @@
         if (ps != null) {
             PackageParser.Package pkg = ps.pkg;
             if (pkg == null) {
-                if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
+                if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0) {
                     return null;
                 }
                 // Only data remains, so we aren't worried about code paths
@@ -3068,6 +3046,7 @@
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
+        flags = updateFlagsForApplication(flags, userId, packageName);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get application info");
         // writer
         synchronized (mPackages) {
@@ -3085,7 +3064,7 @@
             if ("android".equals(packageName)||"system".equals(packageName)) {
                 return mAndroidApplication;
             }
-            if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
+            if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
                 return generateApplicationInfoFromSettingsLPw(packageName, flags, userId);
             }
         }
@@ -3101,16 +3080,18 @@
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                int retCode = -1;
+                boolean success = true;
                 synchronized (mInstallLock) {
-                    retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
-                    if (retCode < 0) {
-                        Slog.w(TAG, "Couldn't clear application caches");
+                    try {
+                        mInstaller.freeCache(volumeUuid, freeStorageSize);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Couldn't clear application caches: " + e);
+                        success = false;
                     }
                 }
                 if (observer != null) {
                     try {
-                        observer.onRemoveCompleted(null, (retCode >= 0));
+                        observer.onRemoveCompleted(null, success);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "RemoveException when invoking call back");
                     }
@@ -3128,17 +3109,19 @@
         mHandler.post(new Runnable() {
             public void run() {
                 mHandler.removeCallbacks(this);
-                int retCode = -1;
+                boolean success = true;
                 synchronized (mInstallLock) {
-                    retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
-                    if (retCode < 0) {
-                        Slog.w(TAG, "Couldn't clear application caches");
+                    try {
+                        mInstaller.freeCache(volumeUuid, freeStorageSize);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Couldn't clear application caches: " + e);
+                        success = false;
                     }
                 }
                 if(pi != null) {
                     try {
                         // Callback via pending intent
-                        int code = (retCode >= 0) ? 1 : 0;
+                        int code = success ? 1 : 0;
                         pi.sendIntent(null, code, null,
                                 null, null);
                     } catch (SendIntentException e1) {
@@ -3151,8 +3134,10 @@
 
     void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
         synchronized (mInstallLock) {
-            if (mInstaller.freeCache(volumeUuid, freeStorageSize) < 0) {
-                throw new IOException("Failed to free enough space");
+            try {
+                mInstaller.freeCache(volumeUuid, freeStorageSize);
+            } catch (InstallerException e) {
+                throw new IOException("Failed to free enough space", e);
             }
         }
     }
@@ -3182,46 +3167,101 @@
     }
 
     /**
-     * Augment the given flags depending on current user running state. This is
-     * purposefully done before acquiring {@link #mPackages} lock.
+     * Update given flags based on encryption status of current user.
      */
-    private int augmentFlagsForUser(int flags, int userId, Object cookie) {
-        if (cookie instanceof Intent) {
-            // If intent claims to be triaged, then we're fine with default
-            // matching behavior below
-            final Intent intent = (Intent) cookie;
-            if ((intent.getFlags() & Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED) != 0) {
-                flags |= PackageManager.MATCH_ENCRYPTION_DEFAULT;
-            }
-        }
-
-        if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE_ONLY
-                | PackageManager.MATCH_ENCRYPTION_AWARE_ONLY)) != 0) {
-            // Caller expressed an opinion about what components they want to
-            // see, so fall through and give them what they want
+    private int updateFlags(int flags, int userId) {
+        if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
+                | PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) {
+            // Caller expressed an explicit opinion about what encryption
+            // aware/unaware components they want to see, so fall through and
+            // give them what they want
         } else {
             // Caller expressed no opinion, so match based on user state
             if (isUserKeyUnlocked(userId)) {
                 flags |= PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
             } else {
-                flags |= PackageManager.MATCH_ENCRYPTION_AWARE_ONLY;
-
-                // If we have a system caller that hasn't done their homework to
-                // decide they want this default behavior, yell at them
-                if (DEBUG_ENCRYPTION_AWARE && (Binder.getCallingUid() == Process.SYSTEM_UID)
-                        && ((flags & PackageManager.MATCH_ENCRYPTION_DEFAULT) == 0)) {
-                    Log.v(TAG, "Caller hasn't been triaged for FBE; they asked about " + cookie,
-                            new Throwable());
-                }
+                flags |= PackageManager.MATCH_ENCRYPTION_AWARE;
             }
         }
+
+        // Safe mode means we should ignore any third-party apps
+        if (mSafeMode) {
+            flags |= PackageManager.MATCH_SYSTEM_ONLY;
+        }
+
         return flags;
     }
 
+    /**
+     * Update given flags when being used to request {@link PackageInfo}.
+     */
+    private int updateFlagsForPackage(int flags, int userId, Object cookie) {
+        boolean triaged = true;
+        if ((flags & (PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
+                | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS)) != 0) {
+            // Caller is asking for component details, so they'd better be
+            // asking for specific encryption matching behavior, or be triaged
+            if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
+                    | PackageManager.MATCH_ENCRYPTION_AWARE
+                    | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+                triaged = false;
+            }
+        }
+        if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES
+                | PackageManager.MATCH_SYSTEM_ONLY
+                | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+            triaged = false;
+        }
+        if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
+            Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+                    + " with flags 0x" + Integer.toHexString(flags), new Throwable());
+        }
+        return updateFlags(flags, userId);
+    }
+
+    /**
+     * Update given flags when being used to request {@link ApplicationInfo}.
+     */
+    private int updateFlagsForApplication(int flags, int userId, Object cookie) {
+        return updateFlagsForPackage(flags, userId, cookie);
+    }
+
+    /**
+     * Update given flags when being used to request {@link ComponentInfo}.
+     */
+    private int updateFlagsForComponent(int flags, int userId, Object cookie) {
+        if (cookie instanceof Intent) {
+            if ((((Intent) cookie).getFlags() & Intent.FLAG_DEBUG_TRIAGED_MISSING) != 0) {
+                flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+            }
+        }
+
+        boolean triaged = true;
+        // Caller is asking for component details, so they'd better be
+        // asking for specific encryption matching behavior, or be triaged
+        if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
+                | PackageManager.MATCH_ENCRYPTION_AWARE
+                | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+            triaged = false;
+        }
+        if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
+            Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+                    + " with flags 0x" + Integer.toHexString(flags), new Throwable());
+        }
+        return updateFlags(flags, userId);
+    }
+
+    /**
+     * Update given flags when being used to request {@link ResolveInfo}.
+     */
+    private int updateFlagsForResolve(int flags, int userId, Object cookie) {
+        return updateFlagsForComponent(flags, userId, cookie);
+    }
+
     @Override
     public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, component);
+        flags = updateFlagsForComponent(flags, userId, component);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -3266,7 +3306,7 @@
     @Override
     public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, component);
+        flags = updateFlagsForComponent(flags, userId, component);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get receiver info");
         synchronized (mPackages) {
             PackageParser.Activity a = mReceivers.mActivities.get(component);
@@ -3285,7 +3325,7 @@
     @Override
     public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, component);
+        flags = updateFlagsForComponent(flags, userId, component);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get service info");
         synchronized (mPackages) {
             PackageParser.Service s = mServices.mServices.get(component);
@@ -3304,7 +3344,7 @@
     @Override
     public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, component);
+        flags = updateFlagsForComponent(flags, userId, component);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get provider info");
         synchronized (mPackages) {
             PackageParser.Provider p = mProviders.mProviders.get(component);
@@ -3732,8 +3772,8 @@
 
             final int flags = permissionsState.getPermissionFlags(name, userId);
             if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-                throw new SecurityException("Cannot grant system fixed permission: "
-                        + name + " for package: " + packageName);
+                throw new SecurityException("Cannot grant system fixed permission "
+                        + name + " for package " + packageName);
             }
 
             if (bp.isDevelopment()) {
@@ -3841,8 +3881,8 @@
 
             final int flags = permissionsState.getPermissionFlags(name, userId);
             if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-                throw new SecurityException("Cannot revoke system fixed permission: "
-                        + name + " for package: " + packageName);
+                throw new SecurityException("Cannot revoke system fixed permission "
+                        + name + " for package " + packageName);
             }
 
             if (bp.isDevelopment()) {
@@ -4046,7 +4086,7 @@
                     "canShowRequestPermissionRationale for user " + userId);
         }
 
-        final int uid = getPackageUid(packageName, userId);
+        final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
         if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) {
             return false;
         }
@@ -4429,7 +4469,7 @@
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
         List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
         final ResolveInfo bestChoice =
@@ -4485,6 +4525,9 @@
     private boolean isEphemeralAllowed(
             Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
         // Short circuit and return early if possible.
+        if (DISABLE_EPHEMERAL_APPS) {
+            return false;
+        }
         final int callingUser = UserHandle.getCallingUserId();
         if (callingUser != UserHandle.USER_SYSTEM) {
             return false;
@@ -4647,7 +4690,7 @@
                     ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
                 }
                 final ActivityInfo ai = getActivityInfo(ppa.mComponent,
-                        flags | PackageManager.GET_DISABLED_COMPONENTS, userId);
+                        flags | MATCH_DISABLED_COMPONENTS, userId);
                 if (DEBUG_PREFERRED || debug) {
                     Slog.v(TAG, "Found persistent preferred activity:");
                     if (ai != null) {
@@ -4687,7 +4730,7 @@
             List<ResolveInfo> query, int priority, boolean always,
             boolean removeMatches, boolean debug, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         // writer
         synchronized (mPackages) {
             if (intent.getSelector() != null) {
@@ -4756,7 +4799,7 @@
                             continue;
                         }
                         final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent,
-                                flags | PackageManager.GET_DISABLED_COMPONENTS, userId);
+                                flags | MATCH_DISABLED_COMPONENTS, userId);
                         if (DEBUG_PREFERRED || debug) {
                             Slog.v(TAG, "Found preferred activity:");
                             if (ai != null) {
@@ -4886,7 +4929,7 @@
     public List<ResolveInfo> queryIntentActivities(Intent intent,
             String resolvedType, int flags, int userId) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
         ComponentName comp = intent.getComponent();
         if (comp == null) {
@@ -5370,7 +5413,7 @@
             Intent[] specifics, String[] specificTypes, Intent intent,
             String resolvedType, int flags, int userId) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
                 false, "query intent activity options");
         final String resultsAction = intent.getAction();
@@ -5543,7 +5586,7 @@
     public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
             int userId) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -5580,7 +5623,7 @@
     @Override
     public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
         if (query != null) {
             if (query.size() >= 1) {
@@ -5596,7 +5639,7 @@
     public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
             int userId) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -5634,7 +5677,7 @@
     public List<ResolveInfo> queryIntentContentProviders(
             Intent intent, String resolvedType, int flags, int userId) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
-        flags = augmentFlagsForUser(flags, userId, intent);
+        flags = updateFlagsForResolve(flags, userId, intent);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -5670,8 +5713,9 @@
 
     @Override
     public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
-        final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
-
+        if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+        flags = updateFlagsForPackage(flags, userId, null);
+        final boolean listUninstalled = (flags & MATCH_UNINSTALLED_PACKAGES) != 0;
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "get installed packages");
 
         // writer
@@ -5750,8 +5794,9 @@
     @Override
     public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
             String[] permissions, int flags, int userId) {
-        if (!sUserManager.exists(userId)) return null;
-        final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
+        if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+        flags = updateFlagsForPackage(flags, userId, permissions);
+        final boolean listUninstalled = (flags & MATCH_UNINSTALLED_PACKAGES) != 0;
 
         // writer
         synchronized (mPackages) {
@@ -5777,8 +5822,9 @@
 
     @Override
     public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
-        if (!sUserManager.exists(userId)) return null;
-        final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
+        if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+        flags = updateFlagsForApplication(flags, userId, null);
+        final boolean listUninstalled = (flags & MATCH_UNINSTALLED_PACKAGES) != 0;
 
         // writer
         synchronized (mPackages) {
@@ -5816,6 +5862,10 @@
 
     @Override
     public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return null;
+        }
+
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
                 "getEphemeralApplications");
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
@@ -5834,6 +5884,10 @@
     public boolean isEphemeralApplication(String packageName, int userId) {
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "isEphemeral");
+        if (DISABLE_EPHEMERAL_APPS) {
+            return false;
+        }
+
         if (!isCallerSameApp(packageName)) {
             return false;
         }
@@ -5848,6 +5902,10 @@
 
     @Override
     public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return null;
+        }
+
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "getCookie");
         if (!isCallerSameApp(packageName)) {
@@ -5861,6 +5919,10 @@
 
     @Override
     public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return true;
+        }
+
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "setCookie");
         if (!isCallerSameApp(packageName)) {
@@ -5874,6 +5936,10 @@
 
     @Override
     public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
+        if (DISABLE_EPHEMERAL_APPS) {
+            return null;
+        }
+
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
                 "getEphemeralApplicationIcon");
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
@@ -5920,7 +5986,7 @@
     @Override
     public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, name);
+        flags = updateFlagsForComponent(flags, userId, name);
         // reader
         synchronized (mPackages) {
             final PackageParser.Provider provider = mProvidersByAuthority.get(name);
@@ -5929,8 +5995,6 @@
                     : null;
             return ps != null
                     && mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)
-                    && (!mSafeMode || (provider.info.applicationInfo.flags
-                            &ApplicationInfo.FLAG_SYSTEM) != 0)
                     ? PackageParser.generateProviderInfo(provider, flags,
                             ps.readUserState(userId), userId)
                     : null;
@@ -5972,7 +6036,7 @@
         final int userId = processName != null ? UserHandle.getUserId(uid)
                 : UserHandle.getCallingUserId();
         if (!sUserManager.exists(userId)) return null;
-        flags = augmentFlagsForUser(flags, userId, processName);
+        flags = updateFlagsForComponent(flags, userId, processName);
 
         ArrayList<ProviderInfo> finalList = null;
         // reader
@@ -5985,9 +6049,7 @@
                         && (processName == null
                                 || (p.info.processName.equals(processName)
                                         && UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
-                        && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)
-                        && (!mSafeMode
-                                || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+                        && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
                     if (finalList == null) {
                         finalList = new ArrayList<ProviderInfo>(3);
                     }
@@ -6009,8 +6071,7 @@
     }
 
     @Override
-    public InstrumentationInfo getInstrumentationInfo(ComponentName name,
-            int flags) {
+    public InstrumentationInfo getInstrumentationInfo(ComponentName name, int flags) {
         // reader
         synchronized (mPackages) {
             final PackageParser.Instrumentation i = mInstrumentation.get(name);
@@ -6073,7 +6134,9 @@
         }
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
         // TODO: generate idmap for split APKs
-        if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
+        try {
+            mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid);
+        } catch (InstallerException e) {
             Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
                     + opkg.baseCodePath);
             return false;
@@ -6133,11 +6196,7 @@
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                         e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                     logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
-                    if (file.isDirectory()) {
-                        mInstaller.rmPackageDir(file.getAbsolutePath());
-                    } else {
-                        file.delete();
-                    }
+                    removeCodePathLI(file);
                 }
             }
         }
@@ -6205,7 +6264,6 @@
 
         try {
             pp.collectCertificates(pkg, parseFlags);
-            pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
             throw PackageManagerException.from(e);
         }
@@ -6293,7 +6351,7 @@
                             + " ignored: updated version " + ps.versionCode
                             + " better than this " + pkg.mVersionCode);
                     if (!updatedPkg.codePath.equals(scanFile)) {
-                        Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : "
+                        Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
                                 + ps.name + " changing from " + updatedPkg.codePathString
                                 + " to " + scanFile);
                         updatedPkg.codePath = scanFile;
@@ -6418,7 +6476,7 @@
                 baseResourcePath = ps.resourcePathString;
             } else {
                 // Should not happen at all. Just log an error.
-                Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
+                Slog.e(TAG, "Resource path not set for package " + pkg.packageName);
             }
         } else {
             resourcePath = pkg.codePath;
@@ -6667,7 +6725,7 @@
         synchronized (mPackages) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
-                throw new IllegalArgumentException("Missing package: " + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
             }
         }
 
@@ -6702,52 +6760,67 @@
         return true;
     }
 
-    private void createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo)
-            throws PackageManagerException {
-        int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
-        if (res != 0) {
-            throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                    "Failed to install " + packageName + ": " + res);
+    private boolean removeDataDirsLI(String volumeUuid, String packageName) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+
+        boolean res = true;
+        final int[] users = sUserManager.getUserIds();
+        for (int user : users) {
+            try {
+                mInstaller.destroyAppData(volumeUuid, packageName, user, flags);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to delete data directory", e);
+                res = false;
+            }
         }
+        return res;
+    }
+
+    void removeCodePathLI(File codePath) {
+        if (codePath.isDirectory()) {
+            try {
+                mInstaller.rmPackageDir(codePath.getAbsolutePath());
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to remove code path", e);
+            }
+        } else {
+            codePath.delete();
+        }
+    }
+
+    void destroyAppDataLI(String volumeUuid, String packageName, int userId, int flags) {
+        try {
+            mInstaller.destroyAppData(volumeUuid, packageName, userId, flags);
+        } catch (InstallerException e) {
+            Slog.w(TAG, "Failed to destroy app data", e);
+        }
+    }
+
+    void restoreconAppDataLI(String volumeUuid, String packageName, int userId, int flags,
+            int appId, String seinfo) {
+        try {
+            mInstaller.restoreconAppData(volumeUuid, packageName, userId, flags, appId, seinfo);
+        } catch (InstallerException e) {
+            Slog.e(TAG, "Failed to restorecon for " + packageName + ": " + e);
+        }
+    }
+
+    private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
 
         final int[] users = sUserManager.getUserIds();
         for (int user : users) {
-            if (user != 0) {
-                res = mInstaller.createUserData(volumeUuid, packageName,
-                        UserHandle.getUid(user, uid), user, seinfo);
-                if (res != 0) {
-                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                            "Failed to createUserData " + packageName + ": " + res);
-                }
+            try {
+                mInstaller.clearAppData(volumeUuid, packageName, user,
+                        flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to delete code cache directory", e);
             }
         }
     }
 
-    private int removeDataDirsLI(String volumeUuid, String packageName) {
-        int[] users = sUserManager.getUserIds();
-        int res = 0;
-        for (int user : users) {
-            int resInner = mInstaller.remove(volumeUuid, packageName, user);
-            if (resInner < 0) {
-                res = resInner;
-            }
-        }
-
-        return res;
-    }
-
-    private int deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
-        int[] users = sUserManager.getUserIds();
-        int res = 0;
-        for (int user : users) {
-            int resInner = mInstaller.deleteCodeCacheFiles(volumeUuid, packageName, user);
-            if (resInner < 0) {
-                res = resInner;
-            }
-        }
-        return res;
-    }
-
     private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
             PackageParser.Package changingLib) {
         if (file.path != null) {
@@ -7177,7 +7250,7 @@
                                               pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
                             throw new PackageManagerException(
                                     INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
-                                            "Signature mismatch for shared user : "
+                                            "Signature mismatch for shared user: "
                                             + pkgSetting.sharedUser);
                         }
                     }
@@ -7242,108 +7315,8 @@
                 pkg.applicationInfo.uid);
 
         if (pkg != mPlatformPackage) {
-            // This is a normal package, need to make its data directory.
-            final File dataPath = Environment.getDataUserCredentialEncryptedPackageDirectory(
-                    pkg.volumeUuid, UserHandle.USER_SYSTEM, pkg.packageName);
-
-            boolean uidError = false;
-            if (dataPath.exists()) {
-                int currentUid = 0;
-                try {
-                    StructStat stat = Os.stat(dataPath.getPath());
-                    currentUid = stat.st_uid;
-                } catch (ErrnoException e) {
-                    Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
-                }
-
-                // If we have mismatched owners for the data path, we have a problem.
-                if (currentUid != pkg.applicationInfo.uid) {
-                    boolean recovered = false;
-                    if (currentUid == 0) {
-                        // The directory somehow became owned by root.  Wow.
-                        // This is probably because the system was stopped while
-                        // installd was in the middle of messing with its libs
-                        // directory.  Ask installd to fix that.
-                        int ret = mInstaller.fixUid(pkg.volumeUuid, pkgName,
-                                pkg.applicationInfo.uid, pkg.applicationInfo.uid);
-                        if (ret >= 0) {
-                            recovered = true;
-                            String msg = "Package " + pkg.packageName
-                                    + " unexpectedly changed to uid 0; recovered to " +
-                                    + pkg.applicationInfo.uid;
-                            reportSettingsProblem(Log.WARN, msg);
-                        }
-                    }
-                    if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
-                            || (scanFlags&SCAN_BOOTING) != 0)) {
-                        // If this is a system app, we can at least delete its
-                        // current data so the application will still work.
-                        int ret = removeDataDirsLI(pkg.volumeUuid, pkgName);
-                        if (ret >= 0) {
-                            // TODO: Kill the processes first
-                            // Old data gone!
-                            String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
-                                    ? "System package " : "Third party package ";
-                            String msg = prefix + pkg.packageName
-                                    + " has changed from uid: "
-                                    + currentUid + " to "
-                                    + pkg.applicationInfo.uid + "; old data erased";
-                            reportSettingsProblem(Log.WARN, msg);
-                            recovered = true;
-                        }
-                        if (!recovered) {
-                            mHasSystemUidErrors = true;
-                        }
-                    } else if (!recovered) {
-                        // If we allow this install to proceed, we will be broken.
-                        // Abort, abort!
-                        throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
-                                "scanPackageLI");
-                    }
-                    if (!recovered) {
-                        pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
-                            + pkg.applicationInfo.uid + "/fs_"
-                            + currentUid;
-                        pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
-                        pkg.applicationInfo.nativeLibraryRootDir = pkg.applicationInfo.dataDir;
-                        String msg = "Package " + pkg.packageName
-                                + " has mismatched uid: "
-                                + currentUid + " on disk, "
-                                + pkg.applicationInfo.uid + " in settings";
-                        // writer
-                        synchronized (mPackages) {
-                            mSettings.mReadMessages.append(msg);
-                            mSettings.mReadMessages.append('\n');
-                            uidError = true;
-                            if (!pkgSetting.uidError) {
-                                reportSettingsProblem(Log.ERROR, msg);
-                            }
-                        }
-                    }
-                }
-
-                // Ensure that directories are prepared
-                createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
-                        pkg.applicationInfo.seinfo);
-
-                if (mShouldRestoreconData) {
-                    Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
-                    mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
-                            pkg.applicationInfo.seinfo, pkg.applicationInfo.uid);
-                }
-            } else {
-                if (DEBUG_PACKAGE_SCANNING) {
-                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
-                        Log.v(TAG, "Want this data dir: " + dataPath);
-                }
-                createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
-                        pkg.applicationInfo.seinfo);
-            }
-
             // Get all of our default paths setup
             pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
-
-            pkgSetting.uidError = uidError;
         }
 
         final String path = scanFile.getPath();
@@ -7377,42 +7350,6 @@
             setNativeLibraryPaths(pkg);
         }
 
-        if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
-        final int[] userIds = sUserManager.getUserIds();
-        synchronized (mInstallLock) {
-            // Make sure all user data directories are ready to roll; we're okay
-            // if they already exist
-            if (!TextUtils.isEmpty(pkg.volumeUuid)) {
-                for (int userId : userIds) {
-                    if (userId != UserHandle.USER_SYSTEM) {
-                        mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
-                                UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
-                                pkg.applicationInfo.seinfo);
-                    }
-                }
-            }
-
-            // Create a native library symlink only if we have native libraries
-            // and if the native libraries are 32 bit libraries. We do not provide
-            // this symlink for 64 bit libraries.
-            if (pkg.applicationInfo.primaryCpuAbi != null &&
-                    !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
-                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "linkNativeLib");
-                try {
-                    final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
-                    for (int userId : userIds) {
-                        if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
-                                nativeLibPath, userId) < 0) {
-                            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                                    "Failed linking native library dir (user=" + userId + ")");
-                        }
-                    }
-                } finally {
-                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-                }
-            }
-        }
-
         // This is a special case for the "system" package, where the ABI is
         // dictated by the zygote configuration (and init.rc). We should keep track
         // of this ABI so that we can deal with "normal" applications that run under
@@ -7429,7 +7366,7 @@
         if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
             if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
                 Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
-                        " for package: " + pkg.packageName);
+                        " for package " + pkg.packageName);
             }
         }
 
@@ -8136,9 +8073,12 @@
                     ps.primaryCpuAbiString = adjustedAbi;
                     if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                         ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
-                        Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + adjustedAbi);
-                        mInstaller.rmdex(ps.codePathString,
-                                getDexCodeInstructionSet(getPreferredInstructionSet()));
+                        Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi);
+                        try {
+                            mInstaller.rmdex(ps.codePathString,
+                                    getDexCodeInstructionSet(getPreferredInstructionSet()));
+                        } catch (InstallerException ignored) {
+                        }
                     }
                 }
             }
@@ -8374,7 +8314,7 @@
             // 64 bit apps will see a 64 bit primary ABI,
 
             if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
-                Slog.e(TAG, "Package: " + pkg + " has multiple bundled libs, but is not multiarch.");
+                Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
             }
 
             if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
@@ -9255,10 +9195,6 @@
                 return null;
             }
             final PackageParser.Activity activity = info.activity;
-            if (mSafeMode && (activity.info.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) activity.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -9479,10 +9415,6 @@
                 return null;
             }
             final PackageParser.Service service = info.service;
-            if (mSafeMode && (service.info.applicationInfo.flags
-                    &ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) service.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -9702,10 +9634,6 @@
                 return null;
             }
             final PackageParser.Provider provider = info.provider;
-            if (mSafeMode && (provider.info.applicationInfo.flags
-                    & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return null;
-            }
             PackageSetting ps = (PackageSetting) provider.owner.mExtras;
             if (ps == null) {
                 return null;
@@ -10037,7 +9965,7 @@
         }
         final VerificationParams verifParams = new VerificationParams(
                 null, sessionParams.originatingUri, sessionParams.referrerUri,
-                sessionParams.originatingUid, null);
+                sessionParams.originatingUid);
         verifParams.setInstallerUid(installerUid);
 
         final OriginInfo origin;
@@ -10190,7 +10118,7 @@
 
         long callingId = Binder.clearCallingIdentity();
         try {
-            boolean sendAdded = false;
+            boolean installed = false;
 
             // writer
             synchronized (mPackages) {
@@ -10202,11 +10130,14 @@
                     pkgSetting.setInstalled(true, userId);
                     pkgSetting.setHidden(false, userId);
                     mSettings.writePackageRestrictionsLPr(userId);
-                    sendAdded = true;
+                    if (pkgSetting.pkg != null) {
+                        prepareAppDataAfterInstall(pkgSetting.pkg);
+                    }
+                    installed = true;
                 }
             }
 
-            if (sendAdded) {
+            if (installed) {
                 sendPackageAddedForUser(packageName, pkgSetting, userId);
             }
         } finally {
@@ -10620,7 +10551,7 @@
                     throw new SecurityException("Bad object " + obj + " for uid " + uid);
                 }
             } else {
-                throw new SecurityException("Unknown calling uid " + uid);
+                throw new SecurityException("Unknown calling UID: " + uid);
             }
 
             // Verify: can't set installerPackageName to a package that is
@@ -11019,13 +10950,6 @@
                     + " file=" + origin.file + " cid=" + origin.cid + "}";
         }
 
-        public ManifestDigest getManifestDigest() {
-            if (verificationParams == null) {
-                return null;
-            }
-            return verificationParams.getManifestDigest();
-        }
-
         private int installLocationPolicy(PackageInfoLite pkgLite) {
             String packageName = pkgLite.packageName;
             int installLocation = pkgLite.installLocation;
@@ -11140,9 +11064,12 @@
                     final long sizeBytes = mContainerService.calculateInstalledSize(
                             origin.resolvedPath, isForwardLocked(), packageAbiOverride);
 
-                    if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
+                    try {
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold);
                         pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                                 installFlags, packageAbiOverride);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to free cache", e);
                     }
 
                     /*
@@ -11218,7 +11145,8 @@
                  * do, then we'll defer to them to verify the packages.
                  */
                 final int requiredUid = mRequiredVerifierPackage == null ? -1
-                        : getPackageUid(mRequiredVerifierPackage, verifierUser.getIdentifier());
+                        : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+                                verifierUser.getIdentifier());
                 if (!origin.existing && requiredUid != -1
                         && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) {
                     final Intent verification = new Intent(
@@ -11228,9 +11156,9 @@
                             PACKAGE_MIME_TYPE);
                     verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
+                    // Query all live verifiers based on current user state
                     final List<ResolveInfo> receivers = queryIntentReceivers(verification,
-                            PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
-                            verifierUser.getIdentifier());
+                            PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier());
 
                     if (DEBUG_VERIFY) {
                         Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
@@ -11444,7 +11372,6 @@
         final int installFlags;
         final String installerPackageName;
         final String volumeUuid;
-        final ManifestDigest manifestDigest;
         final UserHandle user;
         final String abiOverride;
         final String[] installGrantPermissions;
@@ -11459,7 +11386,7 @@
 
         InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, String installerPackageName, String volumeUuid,
-                ManifestDigest manifestDigest, UserHandle user, String[] instructionSets,
+                UserHandle user, String[] instructionSets,
                 String abiOverride, String[] installGrantPermissions,
                 String traceMethod, int traceCookie) {
             this.origin = origin;
@@ -11468,7 +11395,6 @@
             this.observer = observer;
             this.installerPackageName = installerPackageName;
             this.volumeUuid = volumeUuid;
-            this.manifestDigest = manifestDigest;
             this.user = user;
             this.instructionSets = instructionSets;
             this.abiOverride = abiOverride;
@@ -11541,11 +11467,9 @@
             String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
             for (String codePath : allCodePaths) {
                 for (String dexCodeInstructionSet : dexCodeInstructionSets) {
-                    int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet);
-                    if (retCode < 0) {
-                        Slog.w(TAG, "Couldn't remove dex file for package: "
-                                + " at location " + codePath + ", retcode=" + retCode);
-                        // we don't consider this to be a failure of the core package deletion
+                    try {
+                        mInstaller.rmdex(codePath, dexCodeInstructionSet);
+                    } catch (InstallerException ignored) {
                     }
                 }
             }
@@ -11570,7 +11494,7 @@
         /** New install */
         FileInstallArgs(InstallParams params) {
             super(params.origin, params.move, params.observer, params.installFlags,
-                    params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+                    params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie);
@@ -11581,7 +11505,7 @@
 
         /** Existing install */
         FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
-            super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets,
+            super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
                     null, null, null, 0);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
@@ -11731,11 +11655,7 @@
                 return false;
             }
 
-            if (codeFile.isDirectory()) {
-                mInstaller.rmPackageDir(codeFile.getAbsolutePath());
-            } else {
-                codeFile.delete();
-            }
+            removeCodePathLI(codeFile);
 
             if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
                 resourceFile.delete();
@@ -11808,7 +11728,7 @@
         /** New install */
         AsecInstallArgs(InstallParams params) {
             super(params.origin, params.move, params.observer, params.installFlags,
-                    params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+                    params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie);
@@ -11818,7 +11738,7 @@
         AsecInstallArgs(String fullCodePath, String[] instructionSets,
                         boolean isExternal, boolean isForwardLocked) {
             super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
+                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
                     instructionSets, null, null, null, 0);
             // Hackily pretend we're still looking at a full code path
             if (!fullCodePath.endsWith(RES_FILE_NAME)) {
@@ -11835,7 +11755,7 @@
 
         AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
             super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
+                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
                     instructionSets, null, null, null, 0);
             this.cid = cid;
             setMountPath(PackageHelper.getSdDir(cid));
@@ -12067,8 +11987,8 @@
         @Override
         int doPreCopy() {
             if (isFwdLocked()) {
-                if (!PackageHelper.fixSdPermissions(cid,
-                        getPackageUid(DEFAULT_CONTAINER_PACKAGE, 0), RES_FILE_NAME)) {
+                if (!PackageHelper.fixSdPermissions(cid, getPackageUid(DEFAULT_CONTAINER_PACKAGE,
+                        MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM), RES_FILE_NAME)) {
                     return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
                 }
             }
@@ -12102,7 +12022,7 @@
         /** New install */
         MoveInstallArgs(InstallParams params) {
             super(params.origin, params.move, params.observer, params.installFlags,
-                    params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+                    params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie);
@@ -12112,8 +12032,11 @@
             if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
                     + move.fromUuid + " to " + move.toUuid);
             synchronized (mInstaller) {
-                if (mInstaller.copyCompleteApp(move.fromUuid, move.toUuid, move.packageName,
-                        move.dataAppName, move.appId, move.seinfo) != 0) {
+                try {
+                    mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
+                            move.dataAppName, move.appId, move.seinfo);
+                } catch (InstallerException e) {
+                    Slog.w(TAG, "Failed to move app", e);
                     return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
                 }
             }
@@ -12176,11 +12099,7 @@
             synchronized (mInstallLock) {
                 // Clean up both app data and code
                 removeDataDirsLI(volumeUuid, move.packageName);
-                if (codeFile.isDirectory()) {
-                    mInstaller.rmPackageDir(codeFile.getAbsolutePath());
-                } else {
-                    codeFile.delete();
-                }
+                removeCodePathLI(codeFile);
             }
             return true;
         }
@@ -12344,6 +12263,8 @@
                     System.currentTimeMillis(), user);
 
             updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
+            prepareAppDataAfterInstall(newPackage);
+
             // delete the partially installed application. the data directory will have to be
             // restored if it was already existing
             if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -12500,6 +12421,7 @@
                         scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
                 updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
                         perUserInstalled, res, user);
+                prepareAppDataAfterInstall(newPackage);
                 updatedSettings = true;
             } catch (PackageManagerException e) {
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
@@ -12578,7 +12500,7 @@
             if((oldPkg == null) || (oldPkg.applicationInfo == null) ||
                     (oldPkgSetting == null)) {
                 res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
-                        "Couldn't find package:" + packageName + " information");
+                        "Couldn't find package " + packageName + " information");
                 return;
             }
         }
@@ -12631,6 +12553,7 @@
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                 updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
                         perUserInstalled, res, user);
+                prepareAppDataAfterInstall(newPackage);
                 updatedSettings = true;
             }
 
@@ -12876,35 +12799,6 @@
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
-        /* If the installer passed in a manifest digest, compare it now. */
-        if (args.manifestDigest != null) {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectManifestDigest");
-            try {
-                pp.collectManifestDigest(pkg);
-            } catch (PackageParserException e) {
-                res.setError("Failed collect during installPackageLI", e);
-                return;
-            } finally {
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-            }
-
-            if (DEBUG_INSTALL) {
-                final String parsedManifest = pkg.manifestDigest == null ? "null"
-                        : pkg.manifestDigest.toString();
-                Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
-                        + parsedManifest);
-            }
-
-            if (!args.manifestDigest.equals(pkg.manifestDigest)) {
-                res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
-                return;
-            }
-        } else if (DEBUG_INSTALL) {
-            final String parsedManifest = pkg.manifestDigest == null
-                    ? "null" : pkg.manifestDigest.toString();
-            Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
-        }
-
         // Get rid of all references to package scan path via parser.
         pp = null;
         String oldCodePath = null;
@@ -13099,6 +12993,7 @@
 
         final int verifierUid = getPackageUid(
                 mIntentFilterVerifierComponent.getPackageName(),
+                MATCH_DEBUG_TRIAGED_MISSING,
                 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
 
         mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
@@ -13692,10 +13587,12 @@
         try {
             newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
         } catch (PackageManagerException e) {
-            Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
+            Slog.w(TAG, "Failed to restore system package " + newPs.name + ": " + e.getMessage());
             return false;
         }
 
+        prepareAppDataAfterInstall(newPkg);
+
         // writer
         synchronized (mPackages) {
             PackageSetting ps = mSettings.mPackages.get(newPkg.packageName);
@@ -13889,7 +13786,13 @@
                 outInfo.removedAppId = appId;
                 outInfo.removedUsers = new int[] {removeUser};
             }
-            mInstaller.clearUserData(ps.volumeUuid, packageName, removeUser);
+            // TODO: triage flags as part of 26466827
+            final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            try {
+                mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to delete app data", e);
+            }
             removeKeystoreDataIfNeeded(removeUser, appId);
             schedulePackageCleaning(packageName, removeUser, false);
             synchronized (mPackages) {
@@ -13910,13 +13813,13 @@
 
         boolean ret = false;
         if (isSystemApp(ps)) {
-            if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package:" + ps.name);
+            if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources as well and
             // fall back to existing code in system partition
             ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
                     flags, outInfo, writeSettings);
         } else {
-            if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package:" + ps.name);
+            if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
             // Kill application pre-emptively especially for apps on sd.
             killApplication(packageName, ps.appId, "uninstall pkg");
             ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
@@ -14063,13 +13966,16 @@
         // Always delete data directories for package, even if we found no other
         // record of app. This helps users recover from UID mismatches without
         // resorting to a full data wipe.
-        int retCode = mInstaller.clearUserData(pkg.volumeUuid, packageName, userId);
-        if (retCode < 0) {
-            Slog.w(TAG, "Couldn't remove cache files for package: " + packageName);
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        try {
+            mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags);
+        } catch (InstallerException e) {
+            Slog.w(TAG, "Couldn't remove cache files for package " + packageName, e);
             return false;
         }
 
-        final int appId = pkg.applicationInfo.uid;
+        final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
         removeKeystoreDataIfNeeded(userId, appId);
 
         // Create a native library symlink only if we have native libraries
@@ -14078,9 +13984,11 @@
         if (pkg.applicationInfo.primaryCpuAbi != null &&
                 !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
             final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
-            if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
-                    nativeLibPath, userId) < 0) {
-                Slog.w(TAG, "Failed linking native library dir");
+            try {
+                mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+                        nativeLibPath, userId);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed linking native library dir", e);
                 return false;
             }
         }
@@ -14293,10 +14201,14 @@
             Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
             return false;
         }
-        int retCode = mInstaller.deleteCacheFiles(p.volumeUuid, packageName, userId);
-        if (retCode < 0) {
-            Slog.w(TAG, "Couldn't remove cache files for package: "
-                       + packageName + " u" + userId);
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        try {
+            mInstaller.clearAppData(p.volumeUuid, packageName, userId,
+                    flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+        } catch (InstallerException e) {
+            Slog.w(TAG, "Couldn't remove cache files for package "
+                    + packageName + " u" + userId, e);
             return false;
         }
         return true;
@@ -14390,9 +14302,12 @@
             apkPath = p.baseCodePath;
         }
 
-        int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
-                libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
-        if (res < 0) {
+        // TODO: triage flags as part of 26466827
+        final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+        try {
+            mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath,
+                    libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
+        } catch (InstallerException e) {
             return false;
         }
 
@@ -14740,7 +14655,7 @@
         }
         synchronized (mPackages) {
             Slog.i(TAG, "Adding persistent preferred activity " + activity + " for user " + userId +
-                    " :");
+                    ":");
             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
             mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
                     new PersistentPreferredActivity(filter, activity));
@@ -14809,7 +14724,7 @@
             }
             return;
         }
-
+Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
         // this is supposed to be TAG_PREFERRED_BACKUP
         if (!expectedStartTag.equals(parser.getName())) {
             if (DEBUG_BACKUP) {
@@ -14820,6 +14735,7 @@
 
         // skip interfering stuff, then we're aligned with the backing implementation
         while ((type = parser.next()) == XmlPullParser.TEXT) { }
+Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
         functor.apply(parser, userId);
     }
 
@@ -15007,6 +14923,192 @@
     }
 
     @Override
+    public byte[] getPermissionGrantBackup(int userId) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system may call getPermissionGrantBackup()");
+        }
+
+        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+        try {
+            final XmlSerializer serializer = new FastXmlSerializer();
+            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TAG_PERMISSION_BACKUP);
+
+            synchronized (mPackages) {
+                serializeRuntimePermissionGrantsLPr(serializer, userId);
+            }
+
+            serializer.endTag(null, TAG_PERMISSION_BACKUP);
+            serializer.endDocument();
+            serializer.flush();
+        } catch (Exception e) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Unable to write default apps for backup", e);
+            }
+            return null;
+        }
+
+        return dataStream.toByteArray();
+    }
+
+    @Override
+    public void restorePermissionGrants(byte[] backup, int userId) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system may call restorePermissionGrants()");
+        }
+
+        try {
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
+            restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
+                    new BlobXmlRestorer() {
+                        @Override
+                        public void apply(XmlPullParser parser, int userId)
+                                throws XmlPullParserException, IOException {
+                            synchronized (mPackages) {
+                                processRestoredPermissionGrantsLPr(parser, userId);
+                            }
+                        }
+                    } );
+        } catch (Exception e) {
+            if (DEBUG_BACKUP) {
+                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
+            }
+        }
+    }
+
+    private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
+            throws IOException {
+        serializer.startTag(null, TAG_ALL_GRANTS);
+
+        final int N = mSettings.mPackages.size();
+        for (int i = 0; i < N; i++) {
+            final PackageSetting ps = mSettings.mPackages.valueAt(i);
+            boolean pkgGrantsKnown = false;
+
+            PermissionsState packagePerms = ps.getPermissionsState();
+
+            for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
+                final int grantFlags = state.getFlags();
+                // only look at grants that are not system/policy fixed
+                if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
+                    final boolean isGranted = state.isGranted();
+                    // And only back up the user-twiddled state bits
+                    if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
+                        final String packageName = mSettings.mPackages.keyAt(i);
+                        if (!pkgGrantsKnown) {
+                            serializer.startTag(null, TAG_GRANT);
+                            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+                            pkgGrantsKnown = true;
+                        }
+
+                        final boolean userSet =
+                                (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
+                        final boolean userFixed =
+                                (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
+                        final boolean revoke =
+                                (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
+
+                        serializer.startTag(null, TAG_PERMISSION);
+                        serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
+                        if (isGranted) {
+                            serializer.attribute(null, ATTR_IS_GRANTED, "true");
+                        }
+                        if (userSet) {
+                            serializer.attribute(null, ATTR_USER_SET, "true");
+                        }
+                        if (userFixed) {
+                            serializer.attribute(null, ATTR_USER_FIXED, "true");
+                        }
+                        if (revoke) {
+                            serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
+                        }
+                        serializer.endTag(null, TAG_PERMISSION);
+                    }
+                }
+            }
+
+            if (pkgGrantsKnown) {
+                serializer.endTag(null, TAG_GRANT);
+            }
+        }
+
+        serializer.endTag(null, TAG_ALL_GRANTS);
+    }
+
+    private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
+            throws XmlPullParserException, IOException {
+        String pkgName = null;
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            final String tagName = parser.getName();
+            if (tagName.equals(TAG_GRANT)) {
+                pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "+++ Restoring grants for package " + pkgName);
+                }
+            } else if (tagName.equals(TAG_PERMISSION)) {
+
+                final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED));
+                final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME);
+
+                int newFlagSet = 0;
+                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
+                    newFlagSet |= FLAG_PERMISSION_USER_SET;
+                }
+                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
+                    newFlagSet |= FLAG_PERMISSION_USER_FIXED;
+                }
+                if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
+                    newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                }
+                if (DEBUG_BACKUP) {
+                    Slog.v(TAG, "  + Restoring grant: pkg=" + pkgName + " perm=" + permName
+                            + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet));
+                }
+                final PackageSetting ps = mSettings.mPackages.get(pkgName);
+                if (ps != null) {
+                    // Already installed so we apply the grant immediately
+                    if (DEBUG_BACKUP) {
+                        Slog.v(TAG, "        + already installed; applying");
+                    }
+                    PermissionsState perms = ps.getPermissionsState();
+                    BasePermission bp = mSettings.mPermissions.get(permName);
+                    if (bp != null) {
+                        if (isGranted) {
+                            perms.grantRuntimePermission(bp, userId);
+                        }
+                        if (newFlagSet != 0) {
+                            perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
+                        }
+                    }
+                } else {
+                    // Need to wait for post-restore install to apply the grant
+                    if (DEBUG_BACKUP) {
+                        Slog.v(TAG, "        - not yet installed; saving for later");
+                    }
+                    mSettings.processRestoredPermissionGrantLPr(pkgName, permName,
+                            isGranted, newFlagSet, userId);
+                }
+            } else {
+                PackageManagerService.reportSettingsProblem(Log.WARN,
+                        "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName);
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+
+        scheduleWriteSettingsLocked();
+        mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+    }
+
+    @Override
     public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
             int sourceUserId, int targetUserId, int flags) {
         mContext.enforceCallingOrSelfPermission(
@@ -15145,12 +15247,10 @@
             pkgSetting = mSettings.mPackages.get(packageName);
             if (pkgSetting == null) {
                 if (className == null) {
-                    throw new IllegalArgumentException(
-                            "Unknown package: " + packageName);
+                    throw new IllegalArgumentException("Unknown package: " + packageName);
                 }
                 throw new IllegalArgumentException(
-                        "Unknown component: " + packageName
-                        + "/" + className);
+                        "Unknown component: " + packageName + "/" + className);
             }
             // Allow root and verify that userId is not being specified by a different user
             if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) {
@@ -15732,11 +15832,14 @@
                     pw.print("  Required: ");
                     pw.print(mRequiredVerifierPackage);
                     pw.print(" (uid=");
-                    pw.print(getPackageUid(mRequiredVerifierPackage, 0));
+                    pw.print(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.USER_SYSTEM));
                     pw.println(")");
                 } else if (mRequiredVerifierPackage != null) {
                     pw.print("vrfy,"); pw.print(mRequiredVerifierPackage);
-                    pw.print(","); pw.println(getPackageUid(mRequiredVerifierPackage, 0));
+                    pw.print(",");
+                    pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+                            UserHandle.USER_SYSTEM));
                 }
             }
 
@@ -15751,11 +15854,14 @@
                         pw.print("  Using: ");
                         pw.print(verifierPackageName);
                         pw.print(" (uid=");
-                        pw.print(getPackageUid(verifierPackageName, 0));
+                        pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
+                                UserHandle.USER_SYSTEM));
                         pw.println(")");
                     } else if (verifierPackageName != null) {
                         pw.print("ifv,"); pw.print(verifierPackageName);
-                        pw.print(","); pw.println(getPackageUid(verifierPackageName, 0));
+                        pw.print(",");
+                        pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
+                                UserHandle.USER_SYSTEM));
                     }
                 } else {
                     pw.println();
@@ -16014,6 +16120,10 @@
                 mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
             }
 
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) {
+                mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState);
+            }
+
             if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
                 // XXX should handle packageName != null by dumping only install data that
                 // the given package is involved with.
@@ -16161,10 +16271,6 @@
      */
     public void scanAvailableAsecs() {
         updateExternalMediaStatusInner(true, false, false);
-        if (mShouldRestoreconData) {
-            SELinuxMMAC.setRestoreconDone();
-            mShouldRestoreconData = false;
-        }
     }
 
     /*
@@ -16485,22 +16591,31 @@
     }
 
     private void loadPrivatePackagesInner(VolumeInfo vol) {
+        final String volumeUuid = vol.fsUuid;
+        if (TextUtils.isEmpty(volumeUuid)) {
+            Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring");
+            return;
+        }
+
         final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
 
         final VersionInfo ver;
         final List<PackageSetting> packages;
         synchronized (mPackages) {
-            ver = mSettings.findOrCreateVersion(vol.fsUuid);
-            packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+            ver = mSettings.findOrCreateVersion(volumeUuid);
+            packages = mSettings.getVolumePackagesLPr(volumeUuid);
         }
 
+        // TODO: introduce a new concept similar to "frozen" to prevent these
+        // apps from being launched until after data has been fully reconciled
         for (PackageSetting ps : packages) {
             synchronized (mInstallLock) {
                 final PackageParser.Package pkg;
                 try {
                     pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
                     loaded.add(pkg.applicationInfo);
+
                 } catch (PackageManagerException e) {
                     Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
                 }
@@ -16511,14 +16626,27 @@
             }
         }
 
+        // Reconcile app data for all started/unlocked users
+        final UserManager um = mContext.getSystemService(UserManager.class);
+        for (UserInfo user : um.getUsers()) {
+            if (um.isUserUnlocked(user.id)) {
+                reconcileAppsData(volumeUuid, user.id,
+                        Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE);
+            } else if (um.isUserRunning(user.id)) {
+                reconcileAppsData(volumeUuid, user.id, Installer.FLAG_DE_STORAGE);
+            } else {
+                continue;
+            }
+        }
+
         synchronized (mPackages) {
             int updateFlags = UPDATE_PERMISSIONS_ALL;
             if (ver.sdkVersion != mSdkVersion) {
                 logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
-                        + mSdkVersion + "; regranting permissions for " + vol.fsUuid);
+                        + mSdkVersion + "; regranting permissions for " + volumeUuid);
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, vol.fsUuid, updateFlags);
+            updatePermissionsLPw(null, null, volumeUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
@@ -16540,10 +16668,16 @@
     }
 
     private void unloadPrivatePackagesInner(VolumeInfo vol) {
+        final String volumeUuid = vol.fsUuid;
+        if (TextUtils.isEmpty(volumeUuid)) {
+            Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
+            return;
+        }
+
         final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
         synchronized (mPackages) {
-            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
             for (PackageSetting ps : packages) {
                 if (ps.pkg == null) continue;
 
@@ -16603,7 +16737,11 @@
 
             if (destroyUser) {
                 synchronized (mInstallLock) {
-                    mInstaller.removeUserDataDirs(volumeUuid, userId);
+                    try {
+                        mInstaller.removeUserDataDirs(volumeUuid, userId);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to clean up user dirs", e);
+                    }
                 }
             }
         }
@@ -16623,6 +16761,37 @@
         }
     }
 
+    private void assertPackageKnown(String volumeUuid, String packageName)
+            throws PackageManagerException {
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null) {
+                throw new PackageManagerException("Package " + packageName + " is unknown");
+            } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
+                throw new PackageManagerException(
+                        "Package " + packageName + " found on unknown volume " + volumeUuid
+                                + "; expected volume " + ps.volumeUuid);
+            }
+        }
+    }
+
+    private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
+            throws PackageManagerException {
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null) {
+                throw new PackageManagerException("Package " + packageName + " is unknown");
+            } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
+                throw new PackageManagerException(
+                        "Package " + packageName + " found on unknown volume " + volumeUuid
+                                + "; expected volume " + ps.volumeUuid);
+            } else if (!ps.getInstalled(userId)) {
+                throw new PackageManagerException(
+                        "Package " + packageName + " not installed for user " + userId);
+            }
+        }
+    }
+
     /**
      * Examine all apps present on given mounted volume, and destroy apps that
      * aren't expected, either due to uninstallation or reinstallation on
@@ -16639,40 +16808,222 @@
                 continue;
             }
 
-            boolean destroyApp = false;
-            String packageName = null;
             try {
                 final PackageLite pkg = PackageParser.parsePackageLite(file,
                         PackageParser.PARSE_MUST_BE_APK);
-                packageName = pkg.packageName;
+                assertPackageKnown(volumeUuid, pkg.packageName);
 
-                synchronized (mPackages) {
-                    final PackageSetting ps = mSettings.mPackages.get(packageName);
-                    if (ps == null) {
-                        logCriticalInfo(Log.WARN, "Destroying " + packageName + " on + "
-                                + volumeUuid + " because we found no install record");
-                        destroyApp = true;
-                    } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
-                        logCriticalInfo(Log.WARN, "Destroying " + packageName + " on "
-                                + volumeUuid + " because we expected it on " + ps.volumeUuid);
-                        destroyApp = true;
-                    }
+            } catch (PackageParserException | PackageManagerException e) {
+                logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
+                synchronized (mInstallLock) {
+                    removeCodePathLI(file);
                 }
+            }
+        }
+    }
 
-            } catch (PackageParserException e) {
-                logCriticalInfo(Log.WARN, "Destroying " + file + " due to parse failure: " + e);
-                destroyApp = true;
+    /**
+     * Reconcile all app data for the given user.
+     * <p>
+     * Verifies that directories exist and that ownership and labeling is
+     * correct for all installed apps on all mounted volumes.
+     */
+    void reconcileAppsData(int userId, @StorageFlags int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+            final String volumeUuid = vol.getFsUuid();
+            reconcileAppsData(volumeUuid, userId, flags);
+        }
+    }
+
+    /**
+     * Reconcile all app data on given mounted volume.
+     * <p>
+     * Destroys app data that isn't expected, either due to uninstallation or
+     * reinstallation on another volume.
+     * <p>
+     * Verifies that directories exist and that ownership and labeling is
+     * correct for all installed apps.
+     */
+    private void reconcileAppsData(String volumeUuid, int userId, @StorageFlags int flags) {
+        Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+                + Integer.toHexString(flags));
+
+        final File ceDir = Environment.getDataUserCredentialEncryptedDirectory(volumeUuid, userId);
+        final File deDir = Environment.getDataUserDeviceEncryptedDirectory(volumeUuid, userId);
+
+        boolean restoreconNeeded = false;
+
+        // First look for stale data that doesn't belong, and check if things
+        // have changed since we did our last restorecon
+        if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+            if (!isUserKeyUnlocked(userId)) {
+                throw new RuntimeException(
+                        "Yikes, someone asked us to reconcile CE storage while " + userId
+                                + " was still locked; this would have caused massive data loss!");
             }
 
-            if (destroyApp) {
-                synchronized (mInstallLock) {
-                    if (packageName != null) {
-                        removeDataDirsLI(volumeUuid, packageName);
+            restoreconNeeded |= SELinuxMMAC.isRestoreconNeeded(ceDir);
+
+            final File[] files = FileUtils.listFilesOrEmpty(ceDir);
+            for (File file : files) {
+                final String packageName = file.getName();
+                try {
+                    assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+                } catch (PackageManagerException e) {
+                    logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
+                    synchronized (mInstallLock) {
+                        destroyAppDataLI(volumeUuid, packageName, userId,
+                                Installer.FLAG_CE_STORAGE);
                     }
-                    if (file.isDirectory()) {
-                        mInstaller.rmPackageDir(file.getAbsolutePath());
-                    } else {
-                        file.delete();
+                }
+            }
+        }
+        if ((flags & Installer.FLAG_DE_STORAGE) != 0) {
+            restoreconNeeded |= SELinuxMMAC.isRestoreconNeeded(deDir);
+
+            final File[] files = FileUtils.listFilesOrEmpty(deDir);
+            for (File file : files) {
+                final String packageName = file.getName();
+                try {
+                    assertPackageKnownAndInstalled(volumeUuid, packageName, userId);
+                } catch (PackageManagerException e) {
+                    logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e);
+                    synchronized (mInstallLock) {
+                        destroyAppDataLI(volumeUuid, packageName, userId,
+                                Installer.FLAG_DE_STORAGE);
+                    }
+                }
+            }
+        }
+
+        // Ensure that data directories are ready to roll for all packages
+        // installed for this volume and user
+        final List<PackageSetting> packages;
+        synchronized (mPackages) {
+            packages = mSettings.getVolumePackagesLPr(volumeUuid);
+        }
+        int preparedCount = 0;
+        for (PackageSetting ps : packages) {
+            final String packageName = ps.name;
+            if (ps.pkg == null) {
+                Slog.w(TAG, "Odd, missing scanned package " + packageName);
+                // TODO: might be due to legacy ASEC apps; we should circle back
+                // and reconcile again once they're scanned
+                continue;
+            }
+
+            if (ps.getInstalled(userId)) {
+                prepareAppData(volumeUuid, userId, flags, ps.pkg, restoreconNeeded);
+                preparedCount++;
+            }
+        }
+
+        if (restoreconNeeded) {
+            if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+                SELinuxMMAC.setRestoreconDone(ceDir);
+            }
+            if ((flags & Installer.FLAG_DE_STORAGE) != 0) {
+                SELinuxMMAC.setRestoreconDone(deDir);
+            }
+        }
+
+        Slog.v(TAG, "reconcileAppsData finished " + preparedCount
+                + " packages; restoreconNeeded was " + restoreconNeeded);
+    }
+
+    /**
+     * Prepare app data for the given app just after it was installed or
+     * upgraded. This method carefully only touches users that it's installed
+     * for, and it forces a restorecon to handle any seinfo changes.
+     * <p>
+     * Verifies that directories exist and that ownership and labeling is
+     * correct for all installed apps. If there is an ownership mismatch, it
+     * will try recovering system apps by wiping data; third-party app data is
+     * left intact.
+     */
+    private void prepareAppDataAfterInstall(PackageParser.Package pkg) {
+        final PackageSetting ps;
+        synchronized (mPackages) {
+            ps = mSettings.mPackages.get(pkg.packageName);
+        }
+
+        final UserManager um = mContext.getSystemService(UserManager.class);
+        for (UserInfo user : um.getUsers()) {
+            final int flags;
+            if (um.isUserUnlocked(user.id)) {
+                flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+            } else if (um.isUserRunning(user.id)) {
+                flags = Installer.FLAG_DE_STORAGE;
+            } else {
+                continue;
+            }
+
+            if (ps.getInstalled(user.id)) {
+                // Whenever an app changes, force a restorecon of its data
+                // TODO: when user data is locked, mark that we're still dirty
+                prepareAppData(pkg.volumeUuid, user.id, flags, pkg, true);
+            }
+        }
+    }
+
+    /**
+     * Prepare app data for the given app.
+     * <p>
+     * Verifies that directories exist and that ownership and labeling is
+     * correct for all installed apps. If there is an ownership mismatch, this
+     * will try recovering system apps by wiping data; third-party app data is
+     * left intact.
+     */
+    private void prepareAppData(String volumeUuid, int userId, @StorageFlags int flags,
+            PackageParser.Package pkg, boolean restoreconNeeded) {
+        if (DEBUG_APP_DATA) {
+            Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
+                    + Integer.toHexString(flags) + (restoreconNeeded ? " restoreconNeeded" : ""));
+        }
+
+        final String packageName = pkg.packageName;
+        final ApplicationInfo app = pkg.applicationInfo;
+        final int appId = UserHandle.getAppId(app.uid);
+
+        Preconditions.checkNotNull(app.seinfo);
+
+        synchronized (mInstallLock) {
+            try {
+                mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+                        appId, app.seinfo);
+            } catch (InstallerException e) {
+                if (app.isSystemApp()) {
+                    logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+                            + ", but trying to recover: " + e);
+                    destroyAppDataLI(volumeUuid, packageName, userId, flags);
+                    try {
+                        mInstaller.createAppData(volumeUuid, packageName, userId, flags,
+                                appId, app.seinfo);
+                        logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
+                    } catch (InstallerException e2) {
+                        logCriticalInfo(Log.DEBUG, "Recovery failed!");
+                    }
+                } else {
+                    Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
+                }
+            }
+
+            if (restoreconNeeded) {
+                restoreconAppDataLI(volumeUuid, packageName, userId, flags, appId, app.seinfo);
+            }
+
+            if ((flags & Installer.FLAG_CE_STORAGE) != 0) {
+                // Create a native library symlink only if we have native libraries
+                // and if the native libraries are 32 bit libraries. We do not provide
+                // this symlink for 64 bit libraries.
+                if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
+                    final String nativeLibPath = app.nativeLibraryDir;
+                    try {
+                        mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
+                                nativeLibPath, userId);
+                    } catch (InstallerException e) {
+                        Slog.e(TAG, "Failed to link native for " + packageName + ": " + e);
                     }
                 }
             }
@@ -17009,7 +17360,11 @@
             for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
                 final String volumeUuid = vol.getFsUuid();
                 if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
-                mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+                try {
+                    mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+                } catch (InstallerException e) {
+                    Slog.w(TAG, "Failed to remove user data", e);
+                }
             }
             synchronized (mPackages) {
                 removeUnusedPackagesLILPw(userManager, userHandle);
@@ -17072,7 +17427,11 @@
     /** Called by UserManagerService */
     void createNewUser(int userHandle) {
         synchronized (mInstallLock) {
-            mInstaller.createUserConfig(userHandle);
+            try {
+                mInstaller.createUserConfig(userHandle);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to create user config", e);
+            }
             mSettings.createNewUserLI(this, mInstaller, userHandle);
         }
         synchronized (mPackages) {
@@ -17192,7 +17551,7 @@
         synchronized(mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
-                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
             KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -17208,7 +17567,7 @@
         synchronized(mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
-                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
             if (pkg.applicationInfo.uid != Binder.getCallingUid()
@@ -17228,7 +17587,7 @@
         synchronized(mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
-                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
             IBinder ksh = ks.getToken();
@@ -17248,7 +17607,7 @@
         synchronized(mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
-                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
             IBinder ksh = ks.getToken();
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index e7c0ef7..f106b62 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 
 import java.io.File;
@@ -78,4 +79,11 @@
     public boolean isSharedUser() {
         return sharedUser != null;
     }
+
+    public boolean isMatch(int flags) {
+        if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+            return isSystem();
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 903d12b..aa10c08 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -19,20 +19,24 @@
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
 import android.os.Environment;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.Slog;
 import android.util.Xml;
 
 import libcore.io.IoUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -41,9 +45,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 /**
  * Centralized access to SELinux MMAC (middleware MAC) implementation. This
  * class is responsible for loading the appropriate mac_permissions.xml file
@@ -63,42 +64,21 @@
     // to synchronize access during policy load and access attempts.
     private static List<Policy> sPolicies = new ArrayList<>();
 
-    // Data policy override version file.
-    private static final String DATA_VERSION_FILE =
-            Environment.getDataDirectory() + "/security/current/selinux_version";
+    /** Path to version on rootfs */
+    private static final File VERSION_FILE = new File("/selinux_version");
 
-    // Base policy version file.
-    private static final String BASE_VERSION_FILE = "/selinux_version";
+    /** Path to MAC permissions on system image */
+    private static final File MAC_PERMISSIONS = new File(Environment.getRootDirectory(),
+            "/etc/security/mac_permissions.xml");
 
-    // Whether override security policies should be loaded.
-    private static final boolean USE_OVERRIDE_POLICY = useOverridePolicy();
+    /** Path to app contexts on rootfs */
+    private static final File SEAPP_CONTEXTS = new File("/seapp_contexts");
 
-    // Data override mac_permissions.xml policy file.
-    private static final String DATA_MAC_PERMISSIONS =
-            Environment.getDataDirectory() + "/security/current/mac_permissions.xml";
+    /** Calculated hash of {@link #SEAPP_CONTEXTS} */
+    private static final byte[] SEAPP_CONTEXTS_HASH = returnHash(SEAPP_CONTEXTS);
 
-    // Base mac_permissions.xml policy file.
-    private static final String BASE_MAC_PERMISSIONS =
-            Environment.getRootDirectory() + "/etc/security/mac_permissions.xml";
-
-    // Determine which mac_permissions.xml file to use.
-    private static final String MAC_PERMISSIONS = USE_OVERRIDE_POLICY ?
-            DATA_MAC_PERMISSIONS : BASE_MAC_PERMISSIONS;
-
-    // Data override seapp_contexts policy file.
-    private static final String DATA_SEAPP_CONTEXTS =
-            Environment.getDataDirectory() + "/security/current/seapp_contexts";
-
-    // Base seapp_contexts policy file.
-    private static final String BASE_SEAPP_CONTEXTS = "/seapp_contexts";
-
-    // Determine which seapp_contexts file to use.
-    private static final String SEAPP_CONTEXTS = USE_OVERRIDE_POLICY ?
-            DATA_SEAPP_CONTEXTS : BASE_SEAPP_CONTEXTS;
-
-    // Stores the hash of the last used seapp_contexts file.
-    private static final String SEAPP_HASH_FILE =
-            Environment.getDataDirectory().toString() + "/system/seapp_hash";
+    /** Attribute where {@link #SEAPP_CONTEXTS_HASH} is stored */
+    private static final String XATTR_SEAPP_HASH = "user.seapp_hash";
 
     // Append privapp to existing seinfo label
     private static final String PRIVILEGED_APP_STR = ":privapp";
@@ -332,71 +312,42 @@
     }
 
     /**
-     * Determines if a recursive restorecon on /data/data and /data/user is needed.
-     * It does this by comparing the SHA-1 of the seapp_contexts file against the
-     * stored hash at /data/system/seapp_hash.
+     * Determines if a recursive restorecon on the given package data directory
+     * is needed. It does this by comparing the SHA-1 of the seapp_contexts file
+     * against the stored hash in an xattr.
+     * <p>
+     * Note that the xattr isn't in the 'security' namespace, so this should
+     * only be run on directories owned by the system.
      *
      * @return Returns true if the restorecon should occur or false otherwise.
      */
-    public static boolean shouldRestorecon() {
-        // Any error with the seapp_contexts file should be fatal
-        byte[] currentHash = null;
+    public static boolean isRestoreconNeeded(File file) {
         try {
-            currentHash = returnHash(SEAPP_CONTEXTS);
-        } catch (IOException ioe) {
-            Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
-            return false;
+            final byte[] buf = new byte[20];
+            final int len = Os.getxattr(file.getAbsolutePath(), XATTR_SEAPP_HASH, buf);
+            if ((len == 20) && Arrays.equals(SEAPP_CONTEXTS_HASH, buf)) {
+                return false;
+            }
+        } catch (ErrnoException e) {
+            if (e.errno != OsConstants.ENODATA) {
+                Slog.e(TAG, "Failed to read seapp hash for " + file, e);
+            }
         }
 
-        // Push past any error with the stored hash file
-        byte[] storedHash = null;
-        try {
-            storedHash = IoUtils.readFileAsByteArray(SEAPP_HASH_FILE);
-        } catch (IOException ioe) {
-            Slog.w(TAG, "Error opening " + SEAPP_HASH_FILE + ". Assuming first boot.");
-        }
-
-        return (storedHash == null || !MessageDigest.isEqual(storedHash, currentHash));
+        return true;
     }
 
     /**
-     * Stores the SHA-1 of the seapp_contexts to /data/system/seapp_hash.
+     * Stores the SHA-1 of the seapp_contexts into an xattr.
+     * <p>
+     * Note that the xattr isn't in the 'security' namespace, so this should
+     * only be run on directories owned by the system.
      */
-    public static void setRestoreconDone() {
+    public static void setRestoreconDone(File file) {
         try {
-            final byte[] currentHash = returnHash(SEAPP_CONTEXTS);
-            dumpHash(new File(SEAPP_HASH_FILE), currentHash);
-        } catch (IOException ioe) {
-            Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
-        }
-    }
-
-    /**
-     * Dump the contents of a byte array to a specified file.
-     *
-     * @param file The file that receives the byte array content.
-     * @param content A byte array that will be written to the specified file.
-     * @throws IOException if any failed I/O operation occured.
-     *         Included is the failure to atomically rename the tmp
-     *         file used in the process.
-     */
-    private static void dumpHash(File file, byte[] content) throws IOException {
-        FileOutputStream fos = null;
-        File tmp = null;
-        try {
-            tmp = File.createTempFile("seapp_hash", ".journal", file.getParentFile());
-            tmp.setReadable(true);
-            fos = new FileOutputStream(tmp);
-            fos.write(content);
-            fos.getFD().sync();
-            if (!tmp.renameTo(file)) {
-                throw new IOException("Failure renaming " + file.getCanonicalPath());
-            }
-        } finally {
-            if (tmp != null) {
-                tmp.delete();
-            }
-            IoUtils.closeQuietly(fos);
+            Os.setxattr(file.getAbsolutePath(), XATTR_SEAPP_HASH, SEAPP_CONTEXTS_HASH, 0);
+        } catch (ErrnoException e) {
+            Slog.e(TAG, "Failed to persist seapp hash in " + file, e);
         }
     }
 
@@ -405,33 +356,15 @@
      *
      * @param file The path to the file given as a string.
      * @return Returns the SHA-1 of the file as a byte array.
-     * @throws IOException if any failed I/O operations occured.
      */
-    private static byte[] returnHash(String file) throws IOException {
+    private static byte[] returnHash(File file) {
         try {
-            final byte[] contents = IoUtils.readFileAsByteArray(file);
+            final byte[] contents = IoUtils.readFileAsByteArray(file.getAbsolutePath());
             return MessageDigest.getInstance("SHA-1").digest(contents);
-        } catch (NoSuchAlgorithmException nsae) {
-            throw new RuntimeException(nsae);  // impossible
+        } catch (IOException | NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
         }
     }
-
-    private static boolean useOverridePolicy() {
-        try {
-            final String overrideVersion = IoUtils.readFileAsString(DATA_VERSION_FILE);
-            final String baseVersion = IoUtils.readFileAsString(BASE_VERSION_FILE);
-            if (overrideVersion.equals(baseVersion)) {
-                return true;
-            }
-            Slog.e(TAG, "Override policy version '" + overrideVersion + "' doesn't match " +
-                   "base version '" + baseVersion + "'. Skipping override policy files.");
-        } catch (FileNotFoundException fnfe) {
-            // Override version file doesn't have to exist so silently ignore.
-        } catch (IOException ioe) {
-            Slog.w(TAG, "Skipping override policy files.", ioe);
-        }
-        return false;
-    }
 }
 
 /**
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d2a3ede..3dee70d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -16,23 +16,44 @@
 
 package com.android.server.pm;
 
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.os.Process.SYSTEM_UID;
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.os.Process.PACKAGE_INFO_GID;
+import static android.os.Process.SYSTEM_UID;
 import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
 
 import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
 import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageCleanItem;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.content.pm.UserInfo;
+import android.content.pm.VerifierDeviceIdentity;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -47,14 +68,22 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
-import android.util.AtomicFile;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Log;
 import android.util.LogPrinter;
+import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.SparseLongArray;
+import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -65,58 +94,37 @@
 import com.android.server.pm.PackageManagerService.DumpState;
 import com.android.server.pm.PermissionsState.PermissionState;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedWriter;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import java.util.Collection;
+import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
-import android.content.pm.PackageCleanItem;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PermissionInfo;
-import android.content.pm.Signature;
-import android.content.pm.UserInfo;
-import android.content.pm.PackageUserState;
-import android.content.pm.VerifierDeviceIdentity;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.Xml;
-
+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
-import java.util.Map.Entry;
-
-import libcore.io.IoUtils;
 
 /**
  * Holds information about dynamic settings.
@@ -211,6 +219,22 @@
     private static final String ATTR_SDK_VERSION = "sdkVersion";
     private static final String ATTR_DATABASE_VERSION = "databaseVersion";
 
+    // Bookkeeping for restored permission grants
+    private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
+    // package name: ATTR_PACKAGE_NAME
+    private static final String TAG_PERMISSION_ENTRY = "perm";
+    // permission name: ATTR_NAME
+    // permission granted (boolean): ATTR_GRANTED
+    private static final String ATTR_USER_SET = "set";
+    private static final String ATTR_USER_FIXED = "fixed";
+    private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
+
+    // Flag mask of restored permission grants that are applied at install time
+    private static final int USER_RUNTIME_GRANT_MASK =
+            FLAG_PERMISSION_USER_SET
+            | FLAG_PERMISSION_USER_FIXED
+            | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+
     private final Object mLock;
 
     private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
@@ -232,6 +256,26 @@
     private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
             new ArrayMap<String, IntentFilterVerificationInfo>();
 
+    // Bookkeeping for restored user permission grants
+    final class RestoredPermissionGrant {
+        String permissionName;
+        boolean granted;
+        int grantBits;
+
+        RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) {
+            permissionName = name;
+            granted = isGranted;
+            grantBits = theGrantBits;
+        }
+    }
+
+    // This would be more compact as a flat array of restored grants or something, but we
+    // may have quite a few, especially during early device lifetime, and avoiding all those
+    // linear lookups will be important.
+    private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>
+            mRestoredUserGrants =
+                new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>();
+
     private static int mFirstAvailableUid = 0;
 
     /** Map from volume UUID to {@link VersionInfo} */
@@ -382,7 +426,7 @@
         return mPackages.get(name);
     }
 
-    void setInstallStatus(String pkgName, int status) {
+    void setInstallStatus(String pkgName, final int status) {
         PackageSetting p = mPackages.get(pkgName);
         if(p != null) {
             if(p.getInstallStatus() != status) {
@@ -391,6 +435,43 @@
         }
     }
 
+    void applyPendingPermissionGrantsLPw(String packageName, int userId) {
+        ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
+                mRestoredUserGrants.get(userId);
+        if (grantsByPackage == null || grantsByPackage.size() == 0) {
+            return;
+        }
+
+        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName);
+        if (grants == null || grants.size() == 0) {
+            return;
+        }
+
+        final PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.e(TAG, "Can't find supposedly installed package " + packageName);
+            return;
+        }
+        final PermissionsState perms = ps.getPermissionsState();
+
+        for (RestoredPermissionGrant grant : grants) {
+            BasePermission bp = mPermissions.get(grant.permissionName);
+            if (bp != null) {
+                if (grant.granted) {
+                    perms.grantRuntimePermission(bp, userId);
+                }
+                perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits);
+            }
+        }
+
+        // And remove it from the pending-grant bookkeeping
+        grantsByPackage.remove(packageName);
+        if (grantsByPackage.size() < 1) {
+            mRestoredUserGrants.remove(userId);
+        }
+        writeRuntimePermissionsForUserLPr(userId, false);
+    }
+
     void setInstallerPackageName(String pkgName, String installerPkgName) {
         PackageSetting p = mPackages.get(pkgName);
         if (p != null) {
@@ -426,7 +507,7 @@
     boolean disableSystemPackageLPw(String name) {
         final PackageSetting p = mPackages.get(name);
         if(p == null) {
-            Log.w(PackageManagerService.TAG, "Package:"+name+" is not an installed package");
+            Log.w(PackageManagerService.TAG, "Package " + name + " is not an installed package");
             return false;
         }
         final PackageSetting dp = mDisabledSysPackages.get(name);
@@ -451,7 +532,7 @@
     PackageSetting enableSystemPackageLPw(String name) {
         PackageSetting p = mDisabledSysPackages.get(name);
         if(p == null) {
-            Log.w(PackageManagerService.TAG, "Package:"+name+" is not disabled");
+            Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled");
             return null;
         }
         // Reset flag in ApplicationInfo object
@@ -1338,7 +1419,7 @@
             throws XmlPullParserException, IOException {
         IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
         packageSetting.setIntentFilterVerificationInfo(ivi);
-        Log.d(TAG, "Read domain verification for package:" + ivi.getPackageName());
+        Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
     }
 
     private void readRestoredIntentFilterVerifications(XmlPullParser parser)
@@ -1472,7 +1553,7 @@
                     String name = parser.getAttributeValue(null, ATTR_NAME);
                     ps = mPackages.get(name);
                     if (ps == null) {
-                        Slog.w(PackageManagerService.TAG, "No package known for stopped package: "
+                        Slog.w(PackageManagerService.TAG, "No package known for stopped package "
                                 + name);
                         XmlUtils.skipCurrentTag(parser);
                         continue;
@@ -1719,6 +1800,14 @@
         }
     }
 
+    // Specifically for backup/restore
+    public void processRestoredPermissionGrantLPr(String pkgName, String permission,
+            boolean isGranted, int restoredFlagSet, int userId)
+            throws IOException, XmlPullParserException {
+        mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr(
+                pkgName, permission, isGranted, restoredFlagSet, userId);
+    }
+
     void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
             throws IllegalArgumentException, IllegalStateException, IOException {
         serializer.startTag(null, TAG_DEFAULT_APPS);
@@ -2036,7 +2125,7 @@
                         }
                     } else {
                         Slog.w(PackageManagerService.TAG,
-                                "No package known for stopped package: " + name);
+                                "No package known for stopped package " + name);
                     }
                     XmlUtils.skipCurrentTag(parser);
                 } else {
@@ -2857,7 +2946,7 @@
         for (int i=0; i<tmpPa.countCategories(); i++) {
             String cat = tmpPa.getCategory(i);
             if (cat.equals(Intent.CATEGORY_DEFAULT)) {
-                flags |= PackageManager.MATCH_DEFAULT_ONLY;
+                flags |= MATCH_DEFAULT_ONLY;
             } else {
                 intent.addCategory(cat);
             }
@@ -3007,7 +3096,7 @@
                         filter.addCategory(cat);
                     }
                 }
-                if ((flags&PackageManager.MATCH_DEFAULT_ONLY) != 0) {
+                if ((flags & MATCH_DEFAULT_ONLY) != 0) {
                     filter.addCategory(Intent.CATEGORY_DEFAULT);
                 }
                 if (scheme != null) {
@@ -3663,7 +3752,7 @@
             int userHandle) {
         String[] volumeUuids;
         String[] names;
-        int[] uids;
+        int[] appIds;
         String[] seinfos;
         int packagesCount;
         synchronized (mPackages) {
@@ -3671,7 +3760,7 @@
             packagesCount = packages.size();
             volumeUuids = new String[packagesCount];
             names = new String[packagesCount];
-            uids = new int[packagesCount];
+            appIds = new int[packagesCount];
             seinfos = new String[packagesCount];
             Iterator<PackageSetting> packagesIterator = packages.iterator();
             for (int i = 0; i < packagesCount; i++) {
@@ -3685,7 +3774,7 @@
                 // required args and call the installer after mPackages lock has been released
                 volumeUuids[i] = ps.volumeUuid;
                 names[i] = ps.name;
-                uids[i] = UserHandle.getUid(userHandle, ps.appId);
+                appIds[i] = ps.appId;
                 seinfos[i] = ps.pkg.applicationInfo.seinfo;
             }
         }
@@ -3693,7 +3782,14 @@
             if (names[i] == null) {
                 continue;
             }
-            installer.createUserData(volumeUuids[i], names[i], uids[i], userHandle, seinfos[i]);
+            // TODO: triage flags!
+            final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+            try {
+                installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
+                        seinfos[i]);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to prepare app data", e);
+            }
         }
         synchronized (mPackages) {
             applyDefaultPreferredAppsLPw(service, userHandle);
@@ -3794,63 +3890,11 @@
     }
 
     boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
-        return isEnabledLPr(componentInfo, flags, userId)
-                && isMatchLPr(componentInfo, flags);
-    }
+        final PackageSetting ps = mPackages.get(componentInfo.packageName);
+        if (ps == null) return false;
 
-    private boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
-        if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
-            return true;
-        }
-        final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
-        if (PackageManagerService.DEBUG_SETTINGS) {
-            Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
-                    + componentInfo.packageName + " componentName = " + componentInfo.name);
-            Log.v(PackageManagerService.TAG, "enabledComponents: "
-                    + compToString(packageSettings.getEnabledComponents(userId)));
-            Log.v(PackageManagerService.TAG, "disabledComponents: "
-                    + compToString(packageSettings.getDisabledComponents(userId)));
-        }
-        if (packageSettings == null) {
-            return false;
-        }
-        PackageUserState ustate = packageSettings.readUserState(userId);
-        if ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
-            if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
-                return true;
-            }
-        }
-        if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
-                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
-                || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
-                || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
-                    && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
-            return false;
-        }
-        if (ustate.enabledComponents != null
-                && ustate.enabledComponents.contains(componentInfo.name)) {
-            return true;
-        }
-        if (ustate.disabledComponents != null
-                && ustate.disabledComponents.contains(componentInfo.name)) {
-            return false;
-        }
-        return componentInfo.enabled;
-    }
-
-    private boolean isMatchLPr(ComponentInfo componentInfo, int flags) {
-        if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
-            final PackageSetting ps = mPackages.get(componentInfo.packageName);
-            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                return false;
-            }
-        }
-
-        final boolean matchesUnaware = ((flags & PackageManager.MATCH_ENCRYPTION_UNAWARE_ONLY) != 0)
-                && !componentInfo.encryptionAware;
-        final boolean matchesAware = ((flags & PackageManager.MATCH_ENCRYPTION_AWARE_ONLY) != 0)
-                && componentInfo.encryptionAware;
-        return matchesUnaware || matchesAware;
+        final PackageUserState userState = ps.readUserState(userId);
+        return userState.isMatch(componentInfo, flags);
     }
 
     String getInstallerPackageNameLPr(String packageName) {
@@ -3932,7 +3976,6 @@
      * given {@link VolumeInfo#fsUuid}.
      */
     List<PackageSetting> getVolumePackagesLPr(String volumeUuid) {
-        Preconditions.checkNotNull(volumeUuid);
         ArrayList<PackageSetting> res = new ArrayList<>();
         for (int i = 0; i < mPackages.size(); i++) {
             final PackageSetting setting = mPackages.valueAt(i);
@@ -4449,6 +4492,48 @@
         pw.print(mReadMessages.toString());
     }
 
+    void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) {
+        if (mRestoredUserGrants.size() > 0) {
+            pw.println();
+            pw.println("Restored (pending) permission grants:");
+            for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) {
+                ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
+                        mRestoredUserGrants.valueAt(userIndex);
+                if (grantsByPackage != null && grantsByPackage.size() > 0) {
+                    final int userId = mRestoredUserGrants.keyAt(userIndex);
+                    pw.print("  User "); pw.println(userId);
+
+                    for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) {
+                        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex);
+                        if (grants != null && grants.size() > 0) {
+                            final String pkgName = grantsByPackage.keyAt(pkgIndex);
+                            pw.print("    "); pw.print(pkgName); pw.println(" :");
+
+                            for (RestoredPermissionGrant g : grants) {
+                                pw.print("      ");
+                                pw.print(g.permissionName);
+                                if (g.granted) {
+                                    pw.print(" GRANTED");
+                                }
+                                if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
+                                    pw.print(" user_set");
+                                }
+                                if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
+                                    pw.print(" user_fixed");
+                                }
+                                if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                    pw.print(" revoke_on_upgrade");
+                                }
+                                pw.println();
+                            }
+                        }
+                    }
+                }
+            }
+            pw.println();
+        }
+    }
+
     private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) {
         if (pkg == null) {
             pw.print("unknown");
@@ -4546,7 +4631,6 @@
 
     private final class RuntimePermissionPersistence {
         private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
-
         private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
 
         private final Handler mHandler = new MyHandler();
@@ -4663,6 +4747,7 @@
                 serializer.setFeature(
                         "http://xmlpull.org/v1/doc/features.html#indent-output", true);
                 serializer.startDocument(null, true);
+
                 serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
 
                 String fingerprint = mFingerprints.get(userId);
@@ -4691,6 +4776,51 @@
                 }
 
                 serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
+
+                // Now any restored permission grants that are waiting for the apps
+                // in question to be installed.  These are stored as per-package
+                // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some
+                // number of individual permission grant entities.
+                if (mRestoredUserGrants.get(userId) != null) {
+                    ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants =
+                            mRestoredUserGrants.get(userId);
+                    if (restoredGrants != null) {
+                        final int pkgCount = restoredGrants.size();
+                        for (int i = 0; i < pkgCount; i++) {
+                            final ArraySet<RestoredPermissionGrant> pkgGrants =
+                                    restoredGrants.valueAt(i);
+                            if (pkgGrants != null && pkgGrants.size() > 0) {
+                                final String pkgName = restoredGrants.keyAt(i);
+                                serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
+                                serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName);
+
+                                final int N = pkgGrants.size();
+                                for (int z = 0; z < N; z++) {
+                                    RestoredPermissionGrant g = pkgGrants.valueAt(z);
+                                    serializer.startTag(null, TAG_PERMISSION_ENTRY);
+                                    serializer.attribute(null, ATTR_NAME, g.permissionName);
+
+                                    if (g.granted) {
+                                        serializer.attribute(null, ATTR_GRANTED, "true");
+                                    }
+
+                                    if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
+                                        serializer.attribute(null, ATTR_USER_SET, "true");
+                                    }
+                                    if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
+                                        serializer.attribute(null, ATTR_USER_FIXED, "true");
+                                    }
+                                    if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
+                                    }
+                                    serializer.endTag(null, TAG_PERMISSION_ENTRY);
+                                }
+                                serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
+                            }
+                        }
+                    }
+                }
+
                 serializer.endDocument();
                 destination.finishWrite(out);
 
@@ -4764,6 +4894,31 @@
             }
         }
 
+        // Backup/restore support
+
+        public void rememberRestoredUserGrantLPr(String pkgName, String permission,
+                boolean isGranted, int restoredFlagSet, int userId) {
+            // This change will be remembered at write-settings time
+            ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
+                    mRestoredUserGrants.get(userId);
+            if (grantsByPackage == null) {
+                grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>();
+                mRestoredUserGrants.put(userId, grantsByPackage);
+            }
+
+            ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName);
+            if (grants == null) {
+                grants = new ArraySet<RestoredPermissionGrant>();
+                grantsByPackage.put(pkgName, grants);
+            }
+
+            RestoredPermissionGrant grant = new RestoredPermissionGrant(permission,
+                    isGranted, restoredFlagSet);
+            grants.add(grant);
+        }
+
+        // Private internals
+
         private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
@@ -4803,6 +4958,46 @@
                         }
                         parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
                     } break;
+
+                    case TAG_RESTORED_RUNTIME_PERMISSIONS: {
+                        final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+                        parseRestoredRuntimePermissionsLPr(parser, pkgName, userId);
+                    } break;
+                }
+            }
+        }
+
+        private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser,
+                final String pkgName, final int userId) throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                switch (parser.getName()) {
+                    case TAG_PERMISSION_ENTRY: {
+                        final String permName = parser.getAttributeValue(null, ATTR_NAME);
+                        final boolean isGranted = "true".equals(
+                                parser.getAttributeValue(null, ATTR_GRANTED));
+
+                        int permBits = 0;
+                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
+                            permBits |= FLAG_PERMISSION_USER_SET;
+                        }
+                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
+                            permBits |= FLAG_PERMISSION_USER_FIXED;
+                        }
+                        if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
+                            permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                        }
+
+                        if (isGranted || permBits != 0) {
+                            rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId);
+                        }
+                    } break;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 13f4826..13d7c35 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -43,6 +43,7 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -59,7 +60,6 @@
 import android.system.OsConstants;
 import android.util.AtomicFile;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -69,11 +69,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
+import com.android.server.pm.Installer.StorageFlags;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -106,7 +108,7 @@
  */
 public class UserManagerService extends IUserManager.Stub {
     private static final String LOG_TAG = "UserManagerService";
-    static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
+    static final boolean DBG = true; // DO NOT SUBMIT WITH TRUE
     private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
 
     private static final String TAG_NAME = "name";
@@ -123,6 +125,8 @@
     private static final String ATTR_USER_VERSION = "version";
     private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
     private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId";
+    private static final String ATTR_SEED_ACCOUNT_NAME = "seedAccountName";
+    private static final String ATTR_SEED_ACCOUNT_TYPE = "seedAccountType";
     private static final String TAG_GUEST_RESTRICTIONS = "guestRestrictions";
     private static final String TAG_USERS = "users";
     private static final String TAG_USER = "user";
@@ -130,6 +134,7 @@
     private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions";
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
+    private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
     private static final String ATTR_KEY = "key";
     private static final String ATTR_VALUE_TYPE = "type";
     private static final String ATTR_MULTIPLE = "m";
@@ -166,6 +171,10 @@
 
     private static final String XATTR_SERIAL = "user.serial";
 
+    // Tron counters
+    private static final String TRON_GUEST_CREATED = "users_guest_created";
+    private static final String TRON_USER_CREATED = "users_user_created";
+
     private final Context mContext;
     private final PackageManagerService mPm;
     private final Object mPackagesLock;
@@ -178,16 +187,35 @@
     private final File mUsersDir;
     private final File mUserListFile;
 
-    @GuardedBy("mUsersLock")
-    private final SparseArray<UserInfo> mUsers = new SparseArray<>();
-
     /**
-     * This collection contains each user's account name if the user chose to set one up
-     * during the initial user creation process.  Keeping this information separate from mUsers
-     * to avoid accidentally leak it.
+     * User-related information that is used for persisting to flash. Only UserInfo is
+     * directly exposed to other system apps.
      */
+    private static class UserData {
+        // Basic user information and properties
+        UserInfo info;
+        // Account name used when there is a strong association between a user and an account
+        String account;
+        // Account information for seeding into a newly created user. This could also be
+        // used for login validation for an existing user, for updating their credentials.
+        // In the latter case, data may not need to be persisted as it is only valid for the
+        // current login session.
+        String seedAccountName;
+        String seedAccountType;
+        PersistableBundle seedAccountOptions;
+        // Whether to perist the seed account information to be available after a boot
+        boolean persistSeedData;
+
+        void clearSeedAccountData() {
+            seedAccountName = null;
+            seedAccountType = null;
+            seedAccountOptions = null;
+            persistSeedData = false;
+        }
+    }
+
     @GuardedBy("mUsersLock")
-    private final SparseArray<String> mUserAccounts = new SparseArray<>();
+    private final SparseArray<UserData> mUsers = new SparseArray<>();
 
     /**
      * User restrictions set via UserManager.  This doesn't include restrictions set by
@@ -271,6 +299,8 @@
     private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners =
             new ArrayList<>();
 
+    private final LockPatternUtils mLockPatternUtils;
+
     private static UserManagerService sInstance;
 
     public static UserManagerService getInstance() {
@@ -315,6 +345,7 @@
         }
         mLocalService = new LocalService();
         LocalServices.addService(UserManagerInternal.class, mLocalService);
+        mLockPatternUtils = new LockPatternUtils(mContext);
     }
 
     void systemReady() {
@@ -323,7 +354,7 @@
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
-                UserInfo ui = mUsers.valueAt(i);
+                UserInfo ui = mUsers.valueAt(i).info;
                 if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
                     partials.add(ui);
                 }
@@ -351,20 +382,25 @@
     public String getUserAccount(int userId) {
         checkManageUserAndAcrossUsersFullPermission("get user account");
         synchronized (mUsersLock) {
-            return mUserAccounts.get(userId);
+            return mUsers.get(userId).account;
         }
     }
 
     @Override
     public void setUserAccount(int userId, String accountName) {
         checkManageUserAndAcrossUsersFullPermission("set user account");
-        UserInfo userToUpdate = null;
+        UserData userToUpdate = null;
         synchronized (mPackagesLock) {
             synchronized (mUsersLock) {
-                String currentAccount = mUserAccounts.get(userId);
+                final UserData userData = mUsers.get(userId);
+                if (userData == null) {
+                    Slog.e(LOG_TAG, "User not found for setting user account: u" + userId);
+                    return;
+                }
+                String currentAccount = userData.account;
                 if (!Objects.equal(currentAccount, accountName)) {
-                    mUserAccounts.put(userId, accountName);
-                    userToUpdate = mUsers.get(userId);
+                    userData.account = accountName;
+                    userToUpdate = userData;
                 }
             }
 
@@ -380,7 +416,7 @@
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
-                UserInfo ui = mUsers.valueAt(i);
+                UserInfo ui = mUsers.valueAt(i).info;
                 if (ui.isPrimary() && !mRemovingUserIds.get(ui.id)) {
                     return ui;
                 }
@@ -396,7 +432,7 @@
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
-                UserInfo ui = mUsers.valueAt(i);
+                UserInfo ui = mUsers.valueAt(i).info;
                 if (ui.partial) {
                     continue;
                 }
@@ -433,7 +469,7 @@
         }
         final int userSize = mUsers.size();
         for (int i = 0; i < userSize; i++) {
-            UserInfo profile = mUsers.valueAt(i);
+            UserInfo profile = mUsers.valueAt(i).info;
             if (!isProfileOf(user, profile)) {
                 continue;
             }
@@ -451,7 +487,7 @@
     @Override
     public int getCredentialOwnerProfile(int userHandle) {
         checkManageUsersPermission("get the credential owner");
-        if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+        if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
             synchronized (mUsersLock) {
                 UserInfo profileParent = getProfileParentLU(userHandle);
                 if (profileParent != null) {
@@ -549,7 +585,7 @@
             }
             if (profile.isQuietModeEnabled() != enableQuietMode) {
                 profile.flags ^= UserInfo.FLAG_QUIET_MODE;
-                writeUserLP(profile);
+                writeUserLP(getUserDataLU(profile.id));
                 changed = true;
             }
         }
@@ -585,7 +621,7 @@
             }
             if (info != null && !info.isEnabled()) {
                 info.flags ^= UserInfo.FLAG_DISABLED;
-                writeUserLP(info);
+                writeUserLP(getUserDataLU(info.id));
             }
         }
     }
@@ -625,13 +661,22 @@
      * Should be locked on mUsers before calling this.
      */
     private UserInfo getUserInfoLU(int userId) {
-        UserInfo ui = mUsers.get(userId);
+        final UserData userData = mUsers.get(userId);
         // If it is partial and not in the process of being removed, return as unknown user.
-        if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
+        if (userData != null && userData.info.partial && !mRemovingUserIds.get(userId)) {
             Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
             return null;
         }
-        return ui;
+        return userData != null ? userData.info : null;
+    }
+
+    private UserData getUserDataLU(int userId) {
+        final UserData userData = mUsers.get(userId);
+        // If it is partial and not in the process of being removed, return as unknown user.
+        if (userData != null && userData.info.partial && !mRemovingUserIds.get(userId)) {
+            return null;
+        }
+        return userData;
     }
 
     /**
@@ -640,6 +685,17 @@
      */
     private UserInfo getUserInfoNoChecks(int userId) {
         synchronized (mUsersLock) {
+            final UserData userData = mUsers.get(userId);
+            return userData != null ? userData.info : null;
+        }
+    }
+
+    /**
+     * Obtains {@link #mUsersLock} and return UserData from mUsers.
+     * <p>No permissions checking or any addition checks are made</p>
+     */
+    private UserData getUserDataNoChecks(int userId) {
+        synchronized (mUsersLock) {
             return mUsers.get(userId);
         }
     }
@@ -654,14 +710,14 @@
         checkManageUsersPermission("rename users");
         boolean changed = false;
         synchronized (mPackagesLock) {
-            UserInfo info = getUserInfoNoChecks(userId);
-            if (info == null || info.partial) {
+            UserData userData = getUserDataNoChecks(userId);
+            if (userData == null || userData.info.partial) {
                 Slog.w(LOG_TAG, "setUserName: unknown user #" + userId);
                 return;
             }
-            if (name != null && !name.equals(info.name)) {
-                info.name = name;
-                writeUserLP(info);
+            if (name != null && !name.equals(userData.info.name)) {
+                userData.info.name = name;
+                writeUserLP(userData);
                 changed = true;
             }
         }
@@ -676,13 +732,13 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mPackagesLock) {
-                UserInfo info = getUserInfoNoChecks(userId);
-                if (info == null || info.partial) {
+                UserData userData = getUserDataNoChecks(userId);
+                if (userData == null || userData.info.partial) {
                     Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
                     return;
                 }
-                writeBitmapLP(info, bitmap);
-                writeUserLP(info);
+                writeBitmapLP(userData.info, bitmap);
+                writeUserLP(userData);
             }
             sendUserInfoChangedBroadcast(userId);
         } finally {
@@ -729,20 +785,20 @@
     public void makeInitialized(int userId) {
         checkManageUsersPermission("makeInitialized");
         boolean scheduleWriteUser = false;
-        UserInfo info;
+        UserData userData;
         synchronized (mUsersLock) {
-            info = mUsers.get(userId);
-            if (info == null || info.partial) {
+            userData = mUsers.get(userId);
+            if (userData == null || userData.info.partial) {
                 Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
                 return;
             }
-            if ((info.flags & UserInfo.FLAG_INITIALIZED) == 0) {
-                info.flags |= UserInfo.FLAG_INITIALIZED;
+            if ((userData.info.flags & UserInfo.FLAG_INITIALIZED) == 0) {
+                userData.info.flags |= UserInfo.FLAG_INITIALIZED;
                 scheduleWriteUser = true;
             }
         }
         if (scheduleWriteUser) {
-            scheduleWriteUser(info);
+            scheduleWriteUser(userData);
         }
     }
 
@@ -818,7 +874,7 @@
                 writeUserListLP();
             }
             if (localChanged) {
-                writeUserLP(getUserInfoNoChecks(userId));
+                writeUserLP(getUserDataNoChecks(userId));
             }
         }
 
@@ -933,7 +989,7 @@
 
             if (!UserRestrictionsUtils.areEqual(prevBaseRestrictions, newRestrictions)) {
                 mBaseUserRestrictions.put(userId, newRestrictions);
-                scheduleWriteUser(getUserInfoNoChecks(userId));
+                scheduleWriteUser(getUserDataNoChecks(userId));
             }
         }
 
@@ -1081,7 +1137,7 @@
         final int totalUserCount = mUsers.size();
         // Skip over users being removed
         for (int i = 0; i < totalUserCount; i++) {
-            UserInfo user = mUsers.valueAt(i);
+            UserInfo user = mUsers.valueAt(i).info;
             if (!mRemovingUserIds.get(user.id)
                     && !user.isGuest() && !user.partial) {
                 aliveUserCount++;
@@ -1199,7 +1255,7 @@
             int type;
             while ((type = parser.next()) != XmlPullParser.START_TAG
                     && type != XmlPullParser.END_DOCUMENT) {
-                ;
+                // Skip
             }
 
             if (type != XmlPullParser.START_TAG) {
@@ -1227,16 +1283,15 @@
                     final String name = parser.getName();
                     if (name.equals(TAG_USER)) {
                         String id = parser.getAttributeValue(null, ATTR_ID);
-                        Pair<UserInfo, String> userPair = readUserLP(Integer.parseInt(id));
 
-                        if (userPair != null) {
-                            UserInfo user = userPair.first;
-                            String account = userPair.second;
+                        UserData userData = readUserLP(Integer.parseInt(id));
+
+                        if (userData != null) {
                             synchronized (mUsersLock) {
-                                mUsers.put(user.id, user);
-                                mUserAccounts.put(user.id, account);
-                                if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
-                                    mNextSerialNumber = user.id + 1;
+                                mUsers.put(userData.info.id, userData);
+                                if (mNextSerialNumber < 0
+                                        || mNextSerialNumber <= userData.info.id) {
+                                    mNextSerialNumber = userData.info.id + 1;
                                 }
                             }
                         }
@@ -1280,20 +1335,21 @@
         int userVersion = mUserVersion;
         if (userVersion < 1) {
             // Assign a proper name for the owner, if not initialized correctly before
-            UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM);
-            if ("Primary".equals(user.name)) {
-                user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
-                scheduleWriteUser(user);
+            UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
+            if ("Primary".equals(userData.info.name)) {
+                userData.info.name =
+                        mContext.getResources().getString(com.android.internal.R.string.owner_name);
+                scheduleWriteUser(userData);
             }
             userVersion = 1;
         }
 
         if (userVersion < 2) {
             // Owner should be marked as initialized
-            UserInfo user = getUserInfoNoChecks(UserHandle.USER_SYSTEM);
-            if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
-                user.flags |= UserInfo.FLAG_INITIALIZED;
-                scheduleWriteUser(user);
+            UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
+            if ((userData.info.flags & UserInfo.FLAG_INITIALIZED) == 0) {
+                userData.info.flags |= UserInfo.FLAG_INITIALIZED;
+                scheduleWriteUser(userData);
             }
             userVersion = 2;
         }
@@ -1312,12 +1368,13 @@
             final boolean splitSystemUser = UserManager.isSplitSystemUser();
             synchronized (mUsersLock) {
                 for (int i = 0; i < mUsers.size(); i++) {
-                    UserInfo user = mUsers.valueAt(i);
+                    UserData userData = mUsers.valueAt(i);
                     // In non-split mode, only user 0 can have restricted profiles
-                    if (!splitSystemUser && user.isRestricted()
-                            && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) {
-                        user.restrictedProfileParentId = UserHandle.USER_SYSTEM;
-                        scheduleWriteUser(user);
+                    if (!splitSystemUser && userData.info.isRestricted()
+                            && (userData.info.restrictedProfileParentId
+                                    == UserInfo.NO_PROFILE_GROUP_ID)) {
+                        userData.info.restrictedProfileParentId = UserHandle.USER_SYSTEM;
+                        scheduleWriteUser(userData);
                     }
                 }
             }
@@ -1347,8 +1404,10 @@
         UserInfo system = new UserInfo(UserHandle.USER_SYSTEM,
                 mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
                 flags);
+        UserData userData = new UserData();
+        userData.info = system;
         synchronized (mUsersLock) {
-            mUsers.put(system.id, system);
+            mUsers.put(system.id, userData);
         }
         mNextSerialNumber = MIN_USER_ID;
         mUserVersion = USER_VERSION;
@@ -1362,17 +1421,17 @@
         initDefaultGuestRestrictions();
 
         writeUserListLP();
-        writeUserLP(system);
+        writeUserLP(userData);
     }
 
-    private void scheduleWriteUser(UserInfo userInfo) {
+    private void scheduleWriteUser(UserData UserData) {
         if (DBG) {
             debug("scheduleWriteUser");
         }
         // No need to wrap it within a lock -- worst case, we'll just post the same message
         // twice.
-        if (!mHandler.hasMessages(WRITE_USER_MSG, userInfo)) {
-            Message msg = mHandler.obtainMessage(WRITE_USER_MSG, userInfo);
+        if (!mHandler.hasMessages(WRITE_USER_MSG, UserData)) {
+            Message msg = mHandler.obtainMessage(WRITE_USER_MSG, UserData);
             mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
         }
     }
@@ -1384,12 +1443,12 @@
      *   <name>Primary</name>
      * </user>
      */
-    private void writeUserLP(UserInfo userInfo) {
+    private void writeUserLP(UserData userData) {
         if (DBG) {
-            debug("writeUserLP " + userInfo);
+            debug("writeUserLP " + userData);
         }
         FileOutputStream fos = null;
-        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + XML_SUFFIX));
+        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userData.info.id + XML_SUFFIX));
         try {
             fos = userFile.startWrite();
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
@@ -1400,6 +1459,7 @@
             serializer.startDocument(null, true);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
+            final UserInfo userInfo = userData.info;
             serializer.startTag(null, TAG_USER);
             serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
             serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
@@ -1424,6 +1484,15 @@
                 serializer.attribute(null, ATTR_RESTRICTED_PROFILE_PARENT_ID,
                         Integer.toString(userInfo.restrictedProfileParentId));
             }
+            // Write seed data
+            if (userData.persistSeedData) {
+                if (userData.seedAccountName != null) {
+                    serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName);
+                }
+                if (userData.seedAccountType != null) {
+                    serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType);
+                }
+            }
             serializer.startTag(null, TAG_NAME);
             serializer.text(userInfo.name);
             serializer.endTag(null, TAG_NAME);
@@ -1434,23 +1503,24 @@
                         mDevicePolicyLocalUserRestrictions.get(userInfo.id),
                         TAG_DEVICE_POLICY_RESTRICTIONS);
             }
-            // Update the account field if it is set.
-            String account;
-            synchronized (mUsersLock) {
-                account = mUserAccounts.get(userInfo.id);
-            }
-            if (account != null) {
+
+            if (userData.account != null) {
                 serializer.startTag(null, TAG_ACCOUNT);
-                serializer.text(account);
+                serializer.text(userData.account);
                 serializer.endTag(null, TAG_ACCOUNT);
             }
 
+            if (userData.persistSeedData && userData.seedAccountOptions != null) {
+                serializer.startTag(null, TAG_SEED_ACCOUNT_OPTIONS);
+                userData.seedAccountOptions.saveToXml(serializer);
+                serializer.endTag(null, TAG_SEED_ACCOUNT_OPTIONS);
+            }
             serializer.endTag(null, TAG_USER);
 
             serializer.endDocument();
             userFile.finishWrite(fos);
         } catch (Exception ioe) {
-            Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+            Slog.e(LOG_TAG, "Error writing user info " + userData.info.id + "\n" + ioe);
             userFile.failWrite(fos);
         }
     }
@@ -1497,7 +1567,7 @@
             synchronized (mUsersLock) {
                 userIdsToWrite = new int[mUsers.size()];
                 for (int i = 0; i < userIdsToWrite.length; i++) {
-                    UserInfo user = mUsers.valueAt(i);
+                    UserInfo user = mUsers.valueAt(i).info;
                     userIdsToWrite[i] = user.id;
                 }
             }
@@ -1517,7 +1587,7 @@
         }
     }
 
-    private Pair<UserInfo, String> readUserLP(int id) {
+    private UserData readUserLP(int id) {
         int flags = 0;
         int serialNumber = id;
         String name = null;
@@ -1529,6 +1599,10 @@
         int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
         boolean partial = false;
         boolean guestToRemove = false;
+        boolean persistSeedData = false;
+        String seedAccountName = null;
+        String seedAccountType = null;
+        PersistableBundle seedAccountOptions = null;
         Bundle baseRestrictions = new Bundle();
         Bundle localRestrictions = new Bundle();
 
@@ -1542,7 +1616,7 @@
             int type;
             while ((type = parser.next()) != XmlPullParser.START_TAG
                     && type != XmlPullParser.END_DOCUMENT) {
-                ;
+                // Skip
             }
 
             if (type != XmlPullParser.START_TAG) {
@@ -1574,6 +1648,12 @@
                     guestToRemove = true;
                 }
 
+                seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME);
+                seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE);
+                if (seedAccountName != null || seedAccountType != null) {
+                    persistSeedData = true;
+                }
+
                 int outerDepth = parser.getDepth();
                 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                        && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -1595,10 +1675,14 @@
                         if (type == XmlPullParser.TEXT) {
                             account = parser.getText();
                         }
+                    } else if (TAG_SEED_ACCOUNT_OPTIONS.equals(tag)) {
+                        seedAccountOptions = PersistableBundle.restoreFromXml(parser);
+                        persistSeedData = true;
                     }
                 }
             }
 
+            // Create the UserInfo object that gets passed around
             UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
             userInfo.serialNumber = serialNumber;
             userInfo.creationTime = creationTime;
@@ -1607,12 +1691,21 @@
             userInfo.guestToRemove = guestToRemove;
             userInfo.profileGroupId = profileGroupId;
             userInfo.restrictedProfileParentId = restrictedProfileParentId;
+
+            // Create the UserData object that's internal to this class
+            UserData userData = new UserData();
+            userData.info = userInfo;
+            userData.account = account;
+            userData.seedAccountName = seedAccountName;
+            userData.seedAccountType = seedAccountType;
+            userData.persistSeedData = persistSeedData;
+            userData.seedAccountOptions = seedAccountOptions;
+
             synchronized (mRestrictionsLock) {
                 mBaseUserRestrictions.put(id, baseRestrictions);
                 mDevicePolicyLocalUserRestrictions.put(id, localRestrictions);
             }
-            return new Pair<>(userInfo, account);
-
+            return userData;
         } catch (IOException ioe) {
         } catch (XmlPullParserException pe) {
         } finally {
@@ -1657,26 +1750,6 @@
     }
 
     /**
-     * Removes all the restrictions files (res_<packagename>) for a given user.
-     * Does not do any permissions checking.
-     */
-    private void cleanAppRestrictions(int userId) {
-        synchronized (mPackagesLock) {
-            File dir = Environment.getUserSystemDirectory(userId);
-            String[] files = dir.list();
-            if (files == null) return;
-            for (String fileName : files) {
-                if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
-                    File resFile = new File(dir, fileName);
-                    if (resFile.exists()) {
-                        resFile.delete();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
      * Removes the app restrictions file for a specific package and user id, if it exists.
      */
     private void cleanAppRestrictionsForPackage(String pkg, int userId) {
@@ -1714,13 +1787,14 @@
         final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
         final long ident = Binder.clearCallingIdentity();
         UserInfo userInfo;
+        UserData userData;
         final int userId;
         try {
             synchronized (mPackagesLock) {
-                UserInfo parent = null;
+                UserData parent = null;
                 if (parentId != UserHandle.USER_NULL) {
                     synchronized (mUsersLock) {
-                        parent = getUserInfoLU(parentId);
+                        parent = getUserDataLU(parentId);
                     }
                     if (parent == null) return null;
                 }
@@ -1749,7 +1823,7 @@
                                 + "specified");
                         return null;
                     }
-                    if (!parent.canHaveProfile()) {
+                    if (!parent.info.canHaveProfile()) {
                         Log.w(LOG_TAG, "Cannot add restricted profile - profiles cannot be "
                                 + "created for the specified parent user id " + parentId);
                         return null;
@@ -1767,7 +1841,7 @@
                     }
                 }
 
-                if (parent != null && parent.isEphemeral()) {
+                if (parent != null && parent.info.isEphemeral()) {
                     flags |= UserInfo.FLAG_EPHEMERAL;
                 }
                 userId = getNextAvailableId();
@@ -1776,24 +1850,26 @@
                 long now = System.currentTimeMillis();
                 userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
                 userInfo.partial = true;
+                userData = new UserData();
+                userData.info = userInfo;
                 Environment.getUserSystemDirectory(userInfo.id).mkdirs();
                 synchronized (mUsersLock) {
-                    mUsers.put(userId, userInfo);
+                    mUsers.put(userId, userData);
                 }
                 writeUserListLP();
                 if (parent != null) {
                     if (isManagedProfile) {
-                        if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
-                            parent.profileGroupId = parent.id;
+                        if (parent.info.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
+                            parent.info.profileGroupId = parent.info.id;
                             writeUserLP(parent);
                         }
-                        userInfo.profileGroupId = parent.profileGroupId;
+                        userInfo.profileGroupId = parent.info.profileGroupId;
                     } else if (isRestricted) {
-                        if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
-                            parent.restrictedProfileParentId = parent.id;
+                        if (parent.info.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
+                            parent.info.restrictedProfileParentId = parent.info.id;
                             writeUserLP(parent);
                         }
-                        userInfo.restrictedProfileParentId = parent.restrictedProfileParentId;
+                        userInfo.restrictedProfileParentId = parent.info.restrictedProfileParentId;
                     }
                 }
             }
@@ -1813,7 +1889,7 @@
             mPm.createNewUser(userId);
             userInfo.partial = false;
             synchronized (mPackagesLock) {
-                writeUserLP(userInfo);
+                writeUserLP(userData);
             }
             updateUserIds();
             Bundle restrictions = new Bundle();
@@ -1830,6 +1906,7 @@
             addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
                     android.Manifest.permission.MANAGE_USERS);
+            MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED : TRON_USER_CREATED, 1);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -1839,6 +1916,7 @@
     /**
      * @hide
      */
+    @Override
     public UserInfo createRestrictedProfile(String name, int parentUserId) {
         checkManageUsersPermission("setupRestrictedProfile");
         final UserInfo user = createProfileForUser(name, UserInfo.FLAG_RESTRICTED, parentUserId);
@@ -1863,7 +1941,7 @@
         synchronized (mUsersLock) {
             final int size = mUsers.size();
             for (int i = 0; i < size; i++) {
-                final UserInfo user = mUsers.valueAt(i);
+                final UserInfo user = mUsers.valueAt(i).info;
                 if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
                     return user;
                 }
@@ -1878,6 +1956,7 @@
      * @param userHandle the userid of the current guest
      * @return whether the user could be marked for deletion
      */
+    @Override
     public boolean markGuestForDeletion(int userHandle) {
         checkManageUsersPermission("Only the system can remove users");
         if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
@@ -1888,15 +1967,15 @@
 
         long ident = Binder.clearCallingIdentity();
         try {
-            final UserInfo user;
+            final UserData userData;
             synchronized (mPackagesLock) {
                 synchronized (mUsersLock) {
-                    user = mUsers.get(userHandle);
-                    if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
+                    userData = mUsers.get(userHandle);
+                    if (userHandle == 0 || userData == null || mRemovingUserIds.get(userHandle)) {
                         return false;
                     }
                 }
-                if (!user.isGuest()) {
+                if (!userData.info.isGuest()) {
                     return false;
                 }
                 // We set this to a guest user that is to be removed. This is a temporary state
@@ -1904,11 +1983,11 @@
                 // removed. This user will still show up in getUserInfo() calls.
                 // If we don't get around to removing this Guest user, it will be purged on next
                 // startup.
-                user.guestToRemove = true;
+                userData.info.guestToRemove = true;
                 // Mark it as disabled, so that it isn't returned any more when
                 // profiles are queried.
-                user.flags |= UserInfo.FLAG_DISABLED;
-                writeUserLP(user);
+                userData.info.flags |= UserInfo.FLAG_DISABLED;
+                writeUserLP(userData);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1921,6 +2000,7 @@
      * after the user's processes have been terminated.
      * @param userHandle the user's id
      */
+    @Override
     public boolean removeUser(int userHandle) {
         checkManageUsersPermission("Only the system can remove users");
         if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
@@ -1931,7 +2011,7 @@
 
         long ident = Binder.clearCallingIdentity();
         try {
-            final UserInfo user;
+            final UserData userData;
             int currentUser = ActivityManager.getCurrentUser();
             if (currentUser == userHandle) {
                 Log.w(LOG_TAG, "Current user cannot be removed");
@@ -1939,8 +2019,8 @@
             }
             synchronized (mPackagesLock) {
                 synchronized (mUsersLock) {
-                    user = mUsers.get(userHandle);
-                    if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
+                    userData = mUsers.get(userHandle);
+                    if (userHandle == 0 || userData == null || mRemovingUserIds.get(userHandle)) {
                         return false;
                     }
 
@@ -1958,18 +2038,18 @@
                 // Set this to a partially created user, so that the user will be purged
                 // on next startup, in case the runtime stops now before stopping and
                 // removing the user completely.
-                user.partial = true;
+                userData.info.partial = true;
                 // Mark it as disabled, so that it isn't returned any more when
                 // profiles are queried.
-                user.flags |= UserInfo.FLAG_DISABLED;
-                writeUserLP(user);
+                userData.info.flags |= UserInfo.FLAG_DISABLED;
+                writeUserLP(userData);
             }
 
-            if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
-                    && user.isManagedProfile()) {
+            if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+                    && userData.info.isManagedProfile()) {
                 // Send broadcast to notify system that the user removed was a
                 // managed user.
-                sendProfileRemovedBroadcast(user.profileGroupId, user.id);
+                sendProfileRemovedBroadcast(userData.info.profileGroupId, userData.info.id);
             }
 
             if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
@@ -2014,6 +2094,7 @@
                                         + userHandle);
                             }
                             new Thread() {
+                                @Override
                                 public void run() {
                                     // Clean up any ActivityManager state
                                     LocalServices.getService(ActivityManagerInternal.class)
@@ -2038,7 +2119,6 @@
         // Remove this user from the list
         synchronized (mUsersLock) {
             mUsers.remove(userHandle);
-            mUserAccounts.delete(userHandle);
             mIsUserManaged.delete(userHandle);
         }
         synchronized (mRestrictionsLock) {
@@ -2055,7 +2135,13 @@
             writeUserListLP();
         }
         updateUserIds();
-        removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
+        File userDir = Environment.getUserSystemDirectory(userHandle);
+        File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
+        if (userDir.renameTo(renamedUserDir)) {
+            removeDirectoryRecursive(renamedUserDir);
+        } else {
+            removeDirectoryRecursive(userDir);
+        }
     }
 
     private void removeDirectoryRecursive(File parent) {
@@ -2117,29 +2203,6 @@
         }
     }
 
-    private void unhideAllInstalledAppsForUser(final int userHandle) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                List<ApplicationInfo> apps =
-                        mPm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES,
-                                userHandle).getList();
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    for (ApplicationInfo appInfo : apps) {
-                        if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0
-                                && (appInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HIDDEN)
-                                        != 0) {
-                            mPm.setApplicationHiddenSettingAsUser(appInfo.packageName, false,
-                                    userHandle);
-                        }
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        });
-    }
     private int getUidForPackage(String packageName) {
         long ident = Binder.clearCallingIdentity();
         try {
@@ -2369,14 +2432,14 @@
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
-                if (!mUsers.valueAt(i).partial) {
+                if (!mUsers.valueAt(i).info.partial) {
                     num++;
                 }
             }
             final int[] newUsers = new int[num];
             int n = 0;
             for (int i = 0; i < userSize; i++) {
-                if (!mUsers.valueAt(i).partial) {
+                if (!mUsers.valueAt(i).info.partial) {
                     newUsers[n++] = mUsers.keyAt(i);
                 }
             }
@@ -2385,28 +2448,41 @@
     }
 
     /**
-     * Called right before a user starts.  This will not be called for the system user.
+     * Called right before a user is started. This gives us a chance to prepare
+     * app storage and apply any user restrictions.
      */
     public void onBeforeStartUser(int userId) {
-        synchronized (mRestrictionsLock) {
-            applyUserRestrictionsLR(userId);
+        mPm.reconcileAppsData(userId, Installer.FLAG_DE_STORAGE);
+
+        if (userId != UserHandle.USER_SYSTEM) {
+            synchronized (mRestrictionsLock) {
+                applyUserRestrictionsLR(userId);
+            }
         }
     }
 
     /**
+     * Called right before a user is unlocked. This gives us a chance to prepare
+     * app storage.
+     */
+    public void onBeforeUnlockUser(int userId) {
+        mPm.reconcileAppsData(userId, Installer.FLAG_CE_STORAGE);
+    }
+
+    /**
      * Make a note of the last started time of a user and do some cleanup.
      * @param userId the user that was just foregrounded
      */
     public void onUserForeground(int userId) {
-        UserInfo user = getUserInfoNoChecks(userId);
-        if (user == null || user.partial) {
+        UserData userData = getUserDataNoChecks(userId);
+        if (userData == null || userData.info.partial) {
             Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
             return;
         }
         long now = System.currentTimeMillis();
         if (now > EPOCH_PLUS_30_YEARS) {
-            user.lastLoggedInTime = now;
-            scheduleWriteUser(user);
+            userData.info.lastLoggedInTime = now;
+            scheduleWriteUser(userData);
         }
     }
 
@@ -2498,6 +2574,91 @@
     }
 
     @Override
+    public void setSeedAccountData(int userId, String accountName, String accountType,
+            PersistableBundle accountOptions, boolean persist) {
+        checkManageUsersPermission("Require MANAGE_USERS permission to set user seed data");
+        synchronized (mPackagesLock) {
+            final UserData userData;
+            synchronized (mUsersLock) {
+                userData = getUserDataLU(userId);
+                if (userData == null) {
+                    Slog.e(LOG_TAG, "No such user for settings seed data u=" + userId);
+                    return;
+                }
+                userData.seedAccountName = accountName;
+                userData.seedAccountType = accountType;
+                userData.seedAccountOptions = accountOptions;
+                userData.persistSeedData = persist;
+            }
+            if (persist) {
+                writeUserLP(userData);
+            }
+        }
+    }
+
+    @Override
+    public String getSeedAccountName() throws RemoteException {
+        checkManageUsersPermission("Cannot get seed account information");
+        synchronized (mUsersLock) {
+            UserData userData = getUserDataLU(UserHandle.getCallingUserId());
+            return userData.seedAccountName;
+        }
+    }
+
+    @Override
+    public String getSeedAccountType() throws RemoteException {
+        checkManageUsersPermission("Cannot get seed account information");
+        synchronized (mUsersLock) {
+            UserData userData = getUserDataLU(UserHandle.getCallingUserId());
+            return userData.seedAccountType;
+        }
+    }
+
+    @Override
+    public PersistableBundle getSeedAccountOptions() throws RemoteException {
+        checkManageUsersPermission("Cannot get seed account information");
+        synchronized (mUsersLock) {
+            UserData userData = getUserDataLU(UserHandle.getCallingUserId());
+            return userData.seedAccountOptions;
+        }
+    }
+
+    @Override
+    public void clearSeedAccountData() throws RemoteException {
+        checkManageUsersPermission("Cannot clear seed account information");
+        synchronized (mPackagesLock) {
+            UserData userData;
+            synchronized (mUsersLock) {
+                userData = getUserDataLU(UserHandle.getCallingUserId());
+                if (userData == null) return;
+                userData.clearSeedAccountData();
+            }
+            writeUserLP(userData);
+        }
+    }
+
+    @Override
+    public boolean someUserHasSeedAccount(String accountName, String accountType)
+            throws RemoteException {
+        checkManageUsersPermission("Cannot check seed account information");
+        synchronized (mUsersLock) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
+                final UserData data = mUsers.valueAt(i);
+                if (data.info.isInitialized()) continue;
+                if (data.seedAccountName == null || !data.seedAccountName.equals(accountName)) {
+                    continue;
+                }
+                if (data.seedAccountType == null || !data.seedAccountType.equals(accountType)) {
+                    continue;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
         (new Shell()).exec(this, in, out, err, args, resultReceiver);
@@ -2554,35 +2715,36 @@
             synchronized (mUsersLock) {
                 pw.println("Users:");
                 for (int i = 0; i < mUsers.size(); i++) {
-                    UserInfo user = mUsers.valueAt(i);
-                    if (user == null) {
+                    UserData userData = mUsers.valueAt(i);
+                    if (userData == null) {
                         continue;
                     }
-                    final int userId = user.id;
-                    pw.print("  "); pw.print(user);
-                    pw.print(" serialNo="); pw.print(user.serialNumber);
+                    UserInfo userInfo = userData.info;
+                    final int userId = userInfo.id;
+                    pw.print("  "); pw.print(userInfo);
+                    pw.print(" serialNo="); pw.print(userInfo.serialNumber);
                     if (mRemovingUserIds.get(userId)) {
                         pw.print(" <removing> ");
                     }
-                    if (user.partial) {
+                    if (userInfo.partial) {
                         pw.print(" <partial>");
                     }
                     pw.println();
                     pw.print("    Created: ");
-                    if (user.creationTime == 0) {
+                    if (userInfo.creationTime == 0) {
                         pw.println("<unknown>");
                     } else {
                         sb.setLength(0);
-                        TimeUtils.formatDuration(now - user.creationTime, sb);
+                        TimeUtils.formatDuration(now - userInfo.creationTime, sb);
                         sb.append(" ago");
                         pw.println(sb);
                     }
                     pw.print("    Last logged in: ");
-                    if (user.lastLoggedInTime == 0) {
+                    if (userInfo.lastLoggedInTime == 0) {
                         pw.println("<unknown>");
                     } else {
                         sb.setLength(0);
-                        TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
+                        TimeUtils.formatDuration(now - userInfo.lastLoggedInTime, sb);
                         sb.append(" ago");
                         pw.println(sb);
                     }
@@ -2591,22 +2753,35 @@
                     pw.println("    Restrictions:");
                     synchronized (mRestrictionsLock) {
                         UserRestrictionsUtils.dumpRestrictions(
-                                pw, "      ", mBaseUserRestrictions.get(user.id));
+                                pw, "      ", mBaseUserRestrictions.get(userInfo.id));
                         pw.println("    Device policy local restrictions:");
                         UserRestrictionsUtils.dumpRestrictions(
-                                pw, "      ", mDevicePolicyLocalUserRestrictions.get(user.id));
+                                pw, "      ", mDevicePolicyLocalUserRestrictions.get(userInfo.id));
                         pw.println("    Effective restrictions:");
                         UserRestrictionsUtils.dumpRestrictions(
-                                pw, "      ", mCachedEffectiveUserRestrictions.get(user.id));
+                                pw, "      ", mCachedEffectiveUserRestrictions.get(userInfo.id));
                     }
-                    pw.println();
-                    String accountName = mUserAccounts.get(userId);
-                    if (accountName != null) {
-                        pw.print("    Account name: " + accountName);
+
+                    if (userData.account != null) {
+                        pw.print("    Account name: " + userData.account);
                         pw.println();
                     }
+
+                    if (userData.seedAccountName != null) {
+                        pw.print("    Seed account name: " + userData.seedAccountName);
+                        pw.println();
+                        if (userData.seedAccountType != null) {
+                            pw.print("         account type: " + userData.seedAccountType);
+                            pw.println();
+                        }
+                        if (userData.seedAccountOptions != null) {
+                            pw.print("         account options exist");
+                            pw.println();
+                        }
+                    }
                 }
             }
+            pw.println();
             pw.println("  Device policy global restrictions:");
             synchronized (mRestrictionsLock) {
                 UserRestrictionsUtils
@@ -2621,6 +2796,10 @@
                 pw.println();
                 pw.println("  Device managed: " + mIsDeviceManaged);
             }
+            // Dump some capabilities
+            pw.println();
+            pw.println("  Max users: " + UserManager.getMaxSupportedUsers());
+            pw.println("  Supports switchable users: " + UserManager.supportsMultipleUsers());
         }
     }
 
@@ -2632,10 +2811,10 @@
                 case WRITE_USER_MSG:
                     removeMessages(WRITE_USER_MSG, msg.obj);
                     synchronized (mPackagesLock) {
-                        int userId = ((UserInfo) msg.obj).id;
-                        UserInfo userInfo = getUserInfoNoChecks(userId);
-                        if (userInfo != null) {
-                            writeUserLP(userInfo);
+                        int userId = ((UserData) msg.obj).info.id;
+                        UserData userData = getUserDataNoChecks(userId);
+                        if (userData != null) {
+                            writeUserLP(userData);
                         }
                     }
             }
@@ -2673,10 +2852,10 @@
                 invalidateEffectiveUserRestrictionsLR(userId);
             }
 
-            final UserInfo userInfo = getUserInfoNoChecks(userId);
+            final UserData userData = getUserDataNoChecks(userId);
             synchronized (mPackagesLock) {
-                if (userInfo != null) {
-                    writeUserLP(userInfo);
+                if (userData != null) {
+                    writeUserLP(userData);
                 } else {
                     Slog.w(LOG_TAG, "UserInfo not found for " + userId);
                 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 9bbc3c1..f0ed790 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -33,6 +33,8 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -40,10 +42,11 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.List;
 import java.util.Set;
 
 /**
- * Utility methods for uesr restrictions.
+ * Utility methods for user restrictions.
  *
  * <p>See {@link UserManagerService} for the method suffixes.
  */
@@ -88,7 +91,8 @@
             UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
             UserManager.DISALLOW_RECORD_AUDIO,
             UserManager.DISALLOW_CAMERA,
-            UserManager.DISALLOW_RUN_IN_BACKGROUND
+            UserManager.DISALLOW_RUN_IN_BACKGROUND,
+            UserManager.DISALLOW_DATA_ROAMING
     );
 
     /**
@@ -113,7 +117,8 @@
             UserManager.DISALLOW_SMS,
             UserManager.DISALLOW_FUN,
             UserManager.DISALLOW_SAFE_BOOT,
-            UserManager.DISALLOW_CREATE_WINDOWS
+            UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_DATA_ROAMING
     );
 
     /**
@@ -315,6 +320,27 @@
                                         .WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
                     }
                     break;
+                case UserManager.DISALLOW_DATA_ROAMING:
+                    if (newValue) {
+                        // DISALLOW_DATA_ROAMING user restriction is set.
+
+                        // Multi sim device.
+                        SubscriptionManager subscriptionManager = new SubscriptionManager(context);
+                        final List<SubscriptionInfo> subscriptionInfoList =
+                            subscriptionManager.getActiveSubscriptionInfoList();
+                        if (subscriptionInfoList != null) {
+                            for (SubscriptionInfo subInfo : subscriptionInfoList) {
+                                android.provider.Settings.Global.putStringForUser(cr,
+                                    android.provider.Settings.Global.DATA_ROAMING
+                                    + subInfo.getSubscriptionId(), "0", userId);
+                            }
+                        }
+
+                        // Single sim device.
+                        android.provider.Settings.Global.putStringForUser(cr,
+                            android.provider.Settings.Global.DATA_ROAMING, "0", userId);
+                    }
+                    break;
                 case UserManager.DISALLOW_SHARE_LOCATION:
                     if (newValue) {
                         android.provider.Settings.Secure.putIntForUser(cr,
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 3eae7fc..a0f20aa 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -388,7 +388,8 @@
                 public void run() {
                     try {
                         // Take an "interactive" bugreport.
-                        ActivityManagerNative.getDefault().requestBugReport(true);
+                        ActivityManagerNative.getDefault().requestBugReport(
+                                ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
                     } catch (RemoteException e) {
                     }
                 }
@@ -404,7 +405,8 @@
             }
             try {
                 // Take a "full" bugreport.
-                ActivityManagerNative.getDefault().requestBugReport(false);
+                ActivityManagerNative.getDefault().requestBugReport(
+                        ActivityManager.BUGREPORT_OPTION_FULL);
             } catch (RemoteException e) {
             }
             return false;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6fcf1d6..806c4ca 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,6 +18,8 @@
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
+import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.view.WindowManager.LayoutParams.*;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
@@ -181,6 +183,8 @@
     static final int LONG_PRESS_HOME_NOTHING = 0;
     static final int LONG_PRESS_HOME_RECENT_SYSTEM_UI = 1;
     static final int LONG_PRESS_HOME_ASSIST = 2;
+    static final int LONG_PRESS_HOME_PICTURE_IN_PICTURE = 3;
+    static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_PICTURE_IN_PICTURE;
 
     static final int DOUBLE_TAP_HOME_NOTHING = 0;
     static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1;
@@ -313,8 +317,10 @@
     boolean mCanHideNavigationBar = false;
     boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
     boolean mNavigationBarOnBottom = true; // is the navigation bar on the bottom *right now*?
-    int[] mNavigationBarHeightForRotation = new int[4];
-    int[] mNavigationBarWidthForRotation = new int[4];
+    int[] mNavigationBarHeightForRotationDefault = new int[4];
+    int[] mNavigationBarWidthForRotationDefault = new int[4];
+    int[] mNavigationBarHeightForRotationInCarMode = new int[4];
+    int[] mNavigationBarWidthForRotationInCarMode = new int[4];
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
     // This is for car dock and this is updated from resource.
@@ -1309,16 +1315,26 @@
         }
     }
 
-    private void handleLongPressOnHome(int deviceId) {
-        if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
-            mHomeConsumed = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+    private void handleLongPressOnHome(int deviceId, KeyEvent event) {
+        if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
+            return;
+        }
+        mHomeConsumed = true;
+        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
 
-            if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
+        switch (mLongPressOnHomeBehavior) {
+            case LONG_PRESS_HOME_RECENT_SYSTEM_UI:
                 toggleRecentApps();
-            } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_ASSIST) {
+                break;
+            case LONG_PRESS_HOME_ASSIST:
                 launchAssistAction(null, deviceId);
-            }
+                break;
+            case LONG_PRESS_HOME_PICTURE_IN_PICTURE:
+                handlePipKey(event);
+                break;
+            default:
+                Log.w(TAG, "Not defined home long press behavior: " + mLongPressOnHomeBehavior);
+                break;
         }
     }
 
@@ -1329,6 +1345,13 @@
         }
     }
 
+    private void handlePipKey(KeyEvent event) {
+        if (DEBUG_INPUT) Log.d(TAG, "handlePipKey event=" + event);
+        Intent intent = new Intent(Intent.ACTION_PICTURE_IN_PICTURE_BUTTON);
+        intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
     private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
         @Override
         public void run() {
@@ -1621,7 +1644,7 @@
         mLongPressOnHomeBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_longPressOnHomeBehavior);
         if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
-                mLongPressOnHomeBehavior > LONG_PRESS_HOME_ASSIST) {
+                mLongPressOnHomeBehavior > LAST_LONG_PRESS_HOME_BEHAVIOR) {
             mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
         }
 
@@ -1674,20 +1697,37 @@
                 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
 
         // Height of the navigation bar when presented horizontally at bottom
-        mNavigationBarHeightForRotation[mPortraitRotation] =
-        mNavigationBarHeightForRotation[mUpsideDownRotation] =
+        mNavigationBarHeightForRotationDefault[mPortraitRotation] =
+        mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavigationBarHeightForRotation[mLandscapeRotation] =
-        mNavigationBarHeightForRotation[mSeascapeRotation] = res.getDimensionPixelSize(
+        mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
+        mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_height_landscape);
 
         // Width of the navigation bar when presented vertically along one side
-        mNavigationBarWidthForRotation[mPortraitRotation] =
-        mNavigationBarWidthForRotation[mUpsideDownRotation] =
-        mNavigationBarWidthForRotation[mLandscapeRotation] =
-        mNavigationBarWidthForRotation[mSeascapeRotation] =
+        mNavigationBarWidthForRotationDefault[mPortraitRotation] =
+        mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
+        mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
+        mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
 
+        // Height of the navigation bar when presented horizontally at bottom
+        mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
+        mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
+                res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_height_car_mode);
+        mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
+        mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
+
+        // Width of the navigation bar when presented vertically along one side
+        mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
+        mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
+        mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
+        mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
+                res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_width_car_mode);
+
         // SystemUI (status bar) layout policy
         int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
         int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
@@ -2239,42 +2279,61 @@
         return windowTypeToLayerLw(TYPE_STATUS_BAR);
     }
 
+    private int getNavigationBarWidth(int rotation, int uiMode) {
+        if ((uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+            return mNavigationBarWidthForRotationInCarMode[rotation];
+        } else {
+            return mNavigationBarWidthForRotationDefault[rotation];
+        }
+    }
+
     @Override
-    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) {
+    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation,
+            int uiMode) {
         if (mHasNavigationBar) {
             // For a basic navigation bar, when we are in landscape mode we place
             // the navigation bar to the side.
             if (mNavigationBarCanMove && fullWidth > fullHeight) {
-                return fullWidth - mNavigationBarWidthForRotation[rotation];
+                return fullWidth - getNavigationBarWidth(rotation, uiMode);
             }
         }
         return fullWidth;
     }
 
+    private int getNavigationBarHeight(int rotation, int uiMode) {
+        if ((uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+            return mNavigationBarHeightForRotationInCarMode[rotation];
+        } else {
+            return mNavigationBarHeightForRotationDefault[rotation];
+        }
+    }
+
     @Override
-    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation) {
+    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation,
+            int uiMode) {
         if (mHasNavigationBar) {
             // For a basic navigation bar, when we are in portrait mode we place
             // the navigation bar to the bottom.
             if (!mNavigationBarCanMove || fullWidth < fullHeight) {
-                return fullHeight - mNavigationBarHeightForRotation[rotation];
+                return fullHeight - getNavigationBarHeight(rotation, uiMode);
             }
         }
         return fullHeight;
     }
 
     @Override
-    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation) {
-        return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation);
+    public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode) {
+        return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode);
     }
 
     @Override
-    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation) {
+    public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode) {
         // There is a separate status bar at the top of the display.  We don't count that as part
         // of the fixed decor, since it can hide; however, for purposes of configurations,
         // we do want to exclude it since applications can't generally use that part
         // of the screen.
-        return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation) - mStatusBarHeight;
+        return getNonDecorDisplayHeight(
+                fullWidth, fullHeight, rotation, uiMode) - mStatusBarHeight;
     }
 
     @Override
@@ -2811,7 +2870,7 @@
                 }
             } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                 if (!keyguardOn) {
-                    handleLongPressOnHome(event.getDeviceId());
+                    handleLongPressOnHome(event.getDeviceId(), event);
                 }
             }
             return -1;
@@ -2879,7 +2938,7 @@
         } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
             if (down) {
                 if (repeatCount == 0) {
-                    showKeyboardShortcutsMenu();
+                    toggleKeyboardShortcutsMenu();
                 }
             }
         } else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
@@ -2963,6 +3022,14 @@
                         UserHandle.CURRENT_OR_SELF);
             }
             return -1;
+        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+            if (mUseTvRouting) {
+                // On TVs volume keys never go to the foreground app.
+                dispatchDirectAudioEvent(event);
+                return -1;
+            }
         } else if (KeyEvent.isMetaKey(keyCode)) {
             if (down) {
                 mPendingMetaAction = true;
@@ -3311,11 +3378,11 @@
         }
     }
 
-    private void showKeyboardShortcutsMenu() {
+    private void toggleKeyboardShortcutsMenu() {
         try {
             IStatusBarService statusbar = getStatusBarService();
             if (statusbar != null) {
-                statusbar.showKeyboardShortcutsMenu();
+                statusbar.toggleKeyboardShortcutsMenu();
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "RemoteException when showing keyboard shortcuts menu", e);
@@ -3381,7 +3448,6 @@
             if (awakenFromDreams) {
                 awakenDreams();
             }
-            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
             hideRecentApps(false, true);
         } else {
             // Otherwise, just launch Home
@@ -3551,7 +3617,7 @@
     /** {@inheritDoc} */
     @Override
     public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
-                              int displayRotation) {
+                              int displayRotation, int uiMode) {
         mDisplayRotation = displayRotation;
         final int overscanLeft, overscanTop, overscanRight, overscanBottom;
         if (isDefaultDisplay) {
@@ -3662,7 +3728,7 @@
             navVisible |= !canHideNavigationBar();
 
             boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
-                    displayRotation, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
+                    displayRotation, uiMode, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
                     navAllowedHidden);
             if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
                     mDockLeft, mDockTop, mDockRight, mDockBottom));
@@ -3741,7 +3807,7 @@
     }
 
     private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
-            int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
+            int uiMode, int overscanRight, int overscanBottom, Rect dcf, boolean navVisible,
             boolean navTranslucent, boolean navAllowedHidden) {
         if (mNavigationBar != null) {
             boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
@@ -3749,11 +3815,11 @@
             // size.  We need to do this directly, instead of relying on
             // it to bubble up from the nav bar, because this needs to
             // change atomically with screen rotations.
-            mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
+            mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
             if (mNavigationBarOnBottom) {
                 // It's a system nav bar or a portrait screen; nav bar goes on bottom.
                 int top = displayHeight - overscanBottom
-                        - mNavigationBarHeightForRotation[displayRotation];
+                        - getNavigationBarHeight(displayRotation, uiMode);
                 mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
                 mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
                 if (transientNavBarShowing) {
@@ -3778,7 +3844,7 @@
             } else {
                 // Landscape screen; nav bar goes to the right.
                 int left = displayWidth - overscanRight
-                        - mNavigationBarWidthForRotation[displayRotation];
+                        - getNavigationBarWidth(displayRotation, uiMode);
                 mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
                 mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
                 if (transientNavBarShowing) {
@@ -3820,6 +3886,10 @@
         return false;
     }
 
+    private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
+        return !mNavigationBarCanMove || displayWidth < displayHeight;
+    }
+
     /** {@inheritDoc} */
     @Override
     public int getSystemDecorLayerLw() {
@@ -4011,7 +4081,13 @@
             cf.top = vf.top = mStableTop;
             cf.right = vf.right = mStableRight;
             vf.bottom = mStableBottom;
-            cf.bottom = mContentBottom;
+
+            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
+                cf.bottom = mContentBottom;
+            } else {
+                cf.bottom = mDockBottom;
+                vf.bottom = mContentBottom;
+            }
         } else {
 
             // Default policy decor for the default display
@@ -4243,6 +4319,7 @@
                         && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
                         && (attrs.type == TYPE_STATUS_BAR
                             || attrs.type == TYPE_TOAST
+                            || attrs.type == TYPE_DOCK_DIVIDER
                             || attrs.type == TYPE_VOICE_INTERACTION_STARTING
                             || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
                             && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) {
@@ -5040,10 +5117,6 @@
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                if (mUseTvRouting) {
-                    // On TVs volume keys never go to the foreground app
-                    result &= ~ACTION_PASS_TO_USER;
-                }
                 if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                     if (down) {
                         if (interactive && !mScreenshotChordVolumeDownKeyTriggered
@@ -5103,19 +5176,17 @@
                             break;
                         }
                     }
-
-                    if ((result & ACTION_PASS_TO_USER) == 0) {
-                        if (mUseTvRouting) {
-                            dispatchDirectAudioEvent(event);
-                        } else {
-                            // If we aren't passing to the user and no one else
-                            // handled it send it to the session manager to
-                            // figure out.
-                            MediaSessionLegacyHelper.getHelper(mContext)
-                                    .sendVolumeKeyEvent(event, true);
-                        }
-                        break;
-                    }
+                }
+                if (mUseTvRouting) {
+                    // On TVs, defer special key handlings to
+                    // {@link interceptKeyBeforeDispatching()}.
+                    result |= ACTION_PASS_TO_USER;
+                } else if ((result & ACTION_PASS_TO_USER) == 0) {
+                    // If we aren't passing to the user and no one else
+                    // handled it send it to the session manager to
+                    // figure out.
+                    MediaSessionLegacyHelper.getHelper(mContext)
+                            .sendVolumeKeyEvent(event, true);
                 }
                 break;
             }
@@ -5891,6 +5962,22 @@
         }
     }
 
+    @Override
+    public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+            Rect outInsets) {
+        outInsets.setEmpty();
+        if (mStatusBar != null) {
+            outInsets.top = mStatusBarHeight;
+        }
+        if (mNavigationBar != null) {
+            if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
+                outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
+            } else {
+                outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
+            }
+        }
+    }
+
     void sendCloseSystemWindows() {
         PhoneWindow.sendCloseSystemWindows(mContext, null);
     }
@@ -6342,6 +6429,7 @@
             if (mLockScreenTimerActive != enable) {
                 if (enable) {
                     if (localLOGV) Log.v(TAG, "setting lockscreen timer");
+                    mHandler.removeCallbacks(mScreenLockTimeout); // remove any pending requests
                     mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
                 } else {
                     if (localLOGV) Log.v(TAG, "clearing lockscreen timer");
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index 9908624..9284442 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
 import com.android.internal.util.XmlUtils;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -47,8 +48,10 @@
     private static final String ATTRIBUTE_CLASS = "class";
     private static final String ATTRIBUTE_SHORTCUT = "shortcut";
     private static final String ATTRIBUTE_CATEGORY = "category";
+    private static final String ATTRIBUTE_SHIFT = "shift";
 
     private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>();
+    private final SparseArray<ShortcutInfo> mShiftShortcuts = new SparseArray<>();
 
     private final Context mContext;
     
@@ -75,17 +78,21 @@
     public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
         ShortcutInfo shortcut = null;
 
+        // If the Shift key is preesed, then search for the shift shortcuts.
+        boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
+        SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts;
+
         // First try the exact keycode (with modifiers).
         int shortcutChar = kcm.get(keyCode, metaState);
         if (shortcutChar != 0) {
-            shortcut = mShortcuts.get(shortcutChar);
+            shortcut = shortcutMap.get(shortcutChar);
         }
 
         // Next try the primary character on that key.
         if (shortcut == null) {
             shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
             if (shortcutChar != 0) {
-                shortcut = mShortcuts.get(shortcutChar);
+                shortcut = shortcutMap.get(shortcutChar);
             }
         }
 
@@ -114,6 +121,7 @@
                 String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS);
                 String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT);
                 String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
+                String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT);
 
                 if (TextUtils.isEmpty(shortcutName)) {
                     Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className);
@@ -121,6 +129,7 @@
                 }
 
                 final int shortcutChar = shortcutName.charAt(0);
+                final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true"));
 
                 final Intent intent;
                 final String title;
@@ -158,7 +167,11 @@
                 }
 
                 ShortcutInfo shortcut = new ShortcutInfo(title, intent);
-                mShortcuts.put(shortcutChar, shortcut);
+                if (isShiftShortcut) {
+                    mShiftShortcuts.put(shortcutChar, shortcut);
+                } else {
+                    mShortcuts.put(shortcutChar, shortcut);
+                }
             }
         } catch (XmlPullParserException e) {
             Log.w(TAG, "Got exception parsing bookmarks.", e);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 29e3c63..549d2dc 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -126,7 +126,7 @@
 
         final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                 resources.getString(com.android.internal.R.string.config_keyguardComponent));
-        intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED);
+        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
         intent.setComponent(keyguardComponent);
 
         if (!context.bindServiceAsUser(intent, mKeyguardConnection,
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6498dd9..290019c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,21 +16,8 @@
 
 package com.android.server.power;
 
-import android.app.ActivityManager;
-import android.util.SparseIntArray;
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.os.BackgroundThread;
-import com.android.server.EventLogTags;
-import com.android.server.ServiceThread;
-import com.android.server.SystemService;
-import com.android.server.am.BatteryStatsService;
-import com.android.server.lights.Light;
-import com.android.server.lights.LightsManager;
-import com.android.server.Watchdog;
-
 import android.Manifest;
-import android.app.AppOpsManager;
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -65,22 +52,32 @@
 import android.service.dreams.DreamManagerInternal;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.view.Display;
 import android.view.WindowManagerPolicy;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+import com.android.server.Watchdog;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.lights.Light;
+import com.android.server.lights.LightsManager;
+import libcore.util.Objects;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 
-import libcore.util.Objects;
-
 import static android.os.PowerManagerInternal.POWER_HINT_INTERACTION;
 import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
-import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
 
 /**
  * The power manager service is responsible for coordinating power management
@@ -771,6 +768,10 @@
                     intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     mContext.sendBroadcast(intent);
+                    // Send internal version that requires signature permission.
+                    mContext.sendBroadcastAsUser(new Intent(
+                            PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL), UserHandle.ALL,
+                            Manifest.permission.DEVICE_POWER);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 4c7f888..e3e1097 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -23,19 +23,16 @@
 import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -43,9 +40,11 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.FileDescriptor;
@@ -53,19 +52,42 @@
 import java.util.List;
 
 /**
- * The search manager service handles the search UI, and maintains a registry of searchable
- * activities.
+ * The search manager service handles the search UI, and maintains a registry of
+ * searchable activities.
  */
 public class SearchManagerService extends ISearchManager.Stub {
-
-    // general debugging support
     private static final String TAG = "SearchManagerService";
 
+    public static class Lifecycle extends SystemService {
+        private SearchManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new SearchManagerService(getContext());
+            publishBinderService(Context.SEARCH_SERVICE, mService);
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mService.onUnlockUser(userHandle);
+        }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            mService.onCleanupUser(userHandle);
+        }
+    }
+
     // Context that the service is running in.
     private final Context mContext;
 
     // This field is initialized lazily in getSearchables(), and then never modified.
-    private final SparseArray<Searchables> mSearchables = new SparseArray<Searchables>();
+    @GuardedBy("mSearchables")
+    private final SparseArray<Searchables> mSearchables = new SparseArray<>();
 
     /**
      * Initializes the Search Manager service in the provided system context.
@@ -75,65 +97,47 @@
      */
     public SearchManagerService(Context context)  {
         mContext = context;
-        IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        mContext.registerReceiver(new BootCompletedReceiver(), filter);
-        mContext.registerReceiver(new UserReceiver(),
-                new IntentFilter(Intent.ACTION_USER_REMOVED));
         new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
+        new GlobalSearchProviderObserver(context.getContentResolver());
     }
 
     private Searchables getSearchables(int userId) {
-        long origId = Binder.clearCallingIdentity();
+        return getSearchables(userId, false);
+    }
+
+    private Searchables getSearchables(int userId, boolean forceUpdate) {
+        final long token = Binder.clearCallingIdentity();
         try {
-            boolean userExists = ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
-                    .getUserInfo(userId) != null;
-            if (!userExists) return null;
+            final UserManager um = mContext.getSystemService(UserManager.class);
+            if (um.getUserInfo(userId) == null) {
+                throw new IllegalStateException("User " + userId + " doesn't exist");
+            }
+            if (!um.isUserUnlocked(userId)) {
+                throw new IllegalStateException("User " + userId + " isn't unlocked");
+            }
         } finally {
-            Binder.restoreCallingIdentity(origId);
+            Binder.restoreCallingIdentity(token);
         }
         synchronized (mSearchables) {
             Searchables searchables = mSearchables.get(userId);
-
             if (searchables == null) {
-                //Log.i(TAG, "Building list of searchable activities for userId=" + userId);
                 searchables = new Searchables(mContext, userId);
-                searchables.buildSearchableList();
+                searchables.updateSearchableList();
                 mSearchables.append(userId, searchables);
+            } else if (forceUpdate) {
+                searchables.updateSearchableList();
             }
             return searchables;
         }
     }
 
-    private void onUserRemoved(int userId) {
-        if (userId != UserHandle.USER_NULL) {
-            synchronized (mSearchables) {
-                mSearchables.remove(userId);
-            }
-        }
+    private void onUnlockUser(int userId) {
+        getSearchables(userId, true);
     }
 
-    /**
-     * Creates the initial searchables list after boot.
-     */
-    private final class BootCompletedReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            new Thread() {
-                @Override
-                public void run() {
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                    mContext.unregisterReceiver(BootCompletedReceiver.this);
-                    getSearchables(0);
-                }
-            }.start();
-        }
-    }
-
-    private final class UserReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+    private void onCleanupUser(int userId) {
+        synchronized (mSearchables) {
+            mSearchables.remove(userId);
         }
     }
 
@@ -158,7 +162,7 @@
                 // Update list of searchable activities
                 for (int i = 0; i < mSearchables.size(); i++) {
                     if (changingUserId == mSearchables.keyAt(i)) {
-                        getSearchables(mSearchables.keyAt(i)).buildSearchableList();
+                        mSearchables.valueAt(i).updateSearchableList();
                         break;
                     }
                 }
@@ -187,14 +191,13 @@
         public void onChange(boolean selfChange) {
             synchronized (mSearchables) {
                 for (int i = 0; i < mSearchables.size(); i++) {
-                    getSearchables(mSearchables.keyAt(i)).buildSearchableList();
+                    mSearchables.valueAt(i).updateSearchableList();
                 }
             }
             Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
         }
-
     }
 
     //
@@ -208,6 +211,7 @@
      * @return Returns a SearchableInfo record describing the parameters of the search,
      * or null if no searchable metadata was available.
      */
+    @Override
     public SearchableInfo getSearchableInfo(final ComponentName launchActivity) {
         if (launchActivity == null) {
             Log.e(TAG, "getSearchableInfo(), activity == null");
@@ -219,10 +223,12 @@
     /**
      * Returns a list of the searchable activities that can be included in global search.
      */
+    @Override
     public List<SearchableInfo> getSearchablesInGlobalSearch() {
         return getSearchables(UserHandle.getCallingUserId()).getSearchablesInGlobalSearchList();
     }
 
+    @Override
     public List<ResolveInfo> getGlobalSearchActivities() {
         return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivities();
     }
@@ -230,6 +236,7 @@
     /**
      * Gets the name of the global search activity.
      */
+    @Override
     public ComponentName getGlobalSearchActivity() {
         return getSearchables(UserHandle.getCallingUserId()).getGlobalSearchActivity();
     }
@@ -237,6 +244,7 @@
     /**
      * Gets the name of the web search activity.
      */
+    @Override
     public ComponentName getWebSearchActivity() {
         return getSearchables(UserHandle.getCallingUserId()).getWebSearchActivity();
     }
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 0ffbb7d..0046fbb 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -200,7 +200,7 @@
      *
      * TODO: sort the list somehow?  UI choice.
      */
-    public void buildSearchableList() {
+    public void updateSearchableList() {
         // These will become the new values at the end of the method
         HashMap<ComponentName, SearchableInfo> newSearchablesMap
                                 = new HashMap<ComponentName, SearchableInfo>();
@@ -215,11 +215,13 @@
 
         long ident = Binder.clearCallingIdentity();
         try {
-            searchList = queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            searchList = queryIntentActivities(intent,
+                    PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
 
             List<ResolveInfo> webSearchInfoList;
             final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH);
-            webSearchInfoList = queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA);
+            webSearchInfoList = queryIntentActivities(webSearchIntent,
+                    PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
 
             // analyze each one, generate a Searchables record, and record
             if (searchList != null || webSearchInfoList != null) {
@@ -282,8 +284,8 @@
         // Step 1 : Query the package manager for a list
         // of activities that can handle the GLOBAL_SEARCH intent.
         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
-        List<ResolveInfo> activities =
-                    queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        List<ResolveInfo> activities = queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
         if (activities != null && !activities.isEmpty()) {
             // Step 2: Rank matching activities according to our heuristics.
             Collections.sort(activities, GLOBAL_SEARCH_RANKER);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index fc27170..2a1f46e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -17,22 +17,20 @@
 package com.android.server.statusbar;
 
 import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
+import android.util.ArrayMap;
 import android.util.Slog;
-
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.statusbar.StatusBarIconList;
 import com.android.server.LocalServices;
 import com.android.server.notification.NotificationDelegate;
 import com.android.server.wm.WindowManagerService;
@@ -56,7 +54,7 @@
     private Handler mHandler = new Handler();
     private NotificationDelegate mNotificationDelegate;
     private volatile IStatusBar mBar;
-    private StatusBarIconList mIcons = new StatusBarIconList();
+    private ArrayMap<String, StatusBarIcon> mIcons = new ArrayMap<>();
 
     // for disabling the status bar
     private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
@@ -96,9 +94,6 @@
         mContext = context;
         mWindowManager = windowManager;
 
-        final Resources res = context.getResources();
-        mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
-
         LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
     }
 
@@ -300,19 +295,14 @@
         enforceStatusBar();
 
         synchronized (mIcons) {
-            int index = mIcons.getSlotIndex(slot);
-            if (index < 0) {
-                throw new SecurityException("invalid status bar icon slot: " + slot);
-            }
-
             StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
                     iconLevel, 0, contentDescription);
             //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
-            mIcons.setIcon(index, icon);
+            mIcons.put(slot, icon);
 
             if (mBar != null) {
                 try {
-                    mBar.setIcon(index, icon);
+                    mBar.setIcon(slot, icon);
                 } catch (RemoteException ex) {
                 }
             }
@@ -320,26 +310,20 @@
     }
 
     @Override
-    public void setIconVisibility(String slot, boolean visible) {
+    public void setIconVisibility(String slot, boolean visibility) {
         enforceStatusBar();
 
         synchronized (mIcons) {
-            int index = mIcons.getSlotIndex(slot);
-            if (index < 0) {
-                throw new SecurityException("invalid status bar icon slot: " + slot);
-            }
-
-            StatusBarIcon icon = mIcons.getIcon(index);
+            StatusBarIcon icon = mIcons.get(slot);
             if (icon == null) {
                 return;
             }
-
-            if (icon.visible != visible) {
-                icon.visible = visible;
+            if (icon.visible != visibility) {
+                icon.visible = visibility;
 
                 if (mBar != null) {
                     try {
-                        mBar.setIcon(index, icon);
+                        mBar.setIcon(slot, icon);
                     } catch (RemoteException ex) {
                     }
                 }
@@ -352,16 +336,11 @@
         enforceStatusBar();
 
         synchronized (mIcons) {
-            int index = mIcons.getSlotIndex(slot);
-            if (index < 0) {
-                throw new SecurityException("invalid status bar icon slot: " + slot);
-            }
-
-            mIcons.removeIcon(index);
+            mIcons.remove(slot);
 
             if (mBar != null) {
                 try {
-                    mBar.removeIcon(index);
+                    mBar.removeIcon(slot);
                 } catch (RemoteException ex) {
                 }
             }
@@ -503,10 +482,10 @@
     }
 
     @Override
-    public void showKeyboardShortcutsMenu() {
+    public void toggleKeyboardShortcutsMenu() {
         if (mBar != null) {
             try {
-                mBar.showKeyboardShortcutsMenu();
+                mBar.toggleKeyboardShortcutsMenu();
             } catch (RemoteException ex) {}
         }
     }
@@ -583,14 +562,17 @@
     // Callbacks from the status bar service.
     // ================================================================================
     @Override
-    public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
-            int switches[], List<IBinder> binders) {
+    public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
+            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders) {
         enforceStatusBarService();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
         mBar = bar;
         synchronized (mIcons) {
-            iconList.copyFrom(mIcons);
+            for (String slot : mIcons.keySet()) {
+                iconSlots.add(slot);
+                iconList.add(mIcons.get(slot));
+            }
         }
         synchronized (mLock) {
             switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
@@ -812,10 +794,6 @@
             return;
         }
 
-        synchronized (mIcons) {
-            mIcons.dump(pw);
-        }
-
         synchronized (mLock) {
             pw.println("  mDisabled1=0x" + Integer.toHexString(mDisabled1));
             pw.println("  mDisabled2=0x" + Integer.toHexString(mDisabled2));
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d888c56..1d498e1 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -290,14 +290,9 @@
     }
 
     public void setDeviceLockedForUser(int userId, boolean locked) {
-        if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
-            UserInfo info = mUserManager.getUserInfo(userId);
-            if (info.isManagedProfile()) {
-                synchronized (mDeviceLockedForUser) {
-                    mDeviceLockedForUser.put(userId, locked);
-                }
-            } else {
-                Log.wtf(TAG, "Requested to change lock state for non-profile user " + userId);
+        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            synchronized (mDeviceLockedForUser) {
+                mDeviceLockedForUser.put(userId, locked);
             }
         }
     }
@@ -669,24 +664,29 @@
         public boolean isDeviceLocked(int userId) throws RemoteException {
             userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
-            if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
-                userId = resolveProfileParent(userId);
-            }
 
-            return isDeviceLockedInner(userId);
+            long token = Binder.clearCallingIdentity();
+            try {
+                if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+                    userId = resolveProfileParent(userId);
+                }
+                return isDeviceLockedInner(userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
         public boolean isDeviceSecure(int userId) throws RemoteException {
             userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
-            if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
-                userId = resolveProfileParent(userId);
-            }
 
             long token = Binder.clearCallingIdentity();
             try {
-                return new LockPatternUtils(mContext).isSecure(userId);
+                if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+                    userId = resolveProfileParent(userId);
+                }
+                return mLockPatternUtils.isSecure(userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -783,6 +783,7 @@
 
         @Override
         public void setDeviceLockedForUser(int userId, boolean value) {
+            enforceReportPermission();
             mHandler.obtainMessage(MSG_SET_DEVICE_LOCKED, value ? 1 : 0, userId)
                     .sendToTarget();
         }
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index a035826..ebbb8b3 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -116,7 +116,7 @@
 
     public void close() {
         synchronized (mLock) {
-            if (mPtr != 0l) {
+            if (mPtr != 0L) {
                 nativeClose(mPtr);
             }
         }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d814ebf..3193ff8 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -465,7 +465,7 @@
         try {
             context = mContext.createPackageContextAsUser("android", 0, user);
         } catch (NameNotFoundException e) {
-            Slog.e(TAG, "failed to create package contenxt as user " + user);
+            Slog.e(TAG, "failed to create package context as user " + user);
             context = mContext;
         }
         return context.getContentResolver();
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 7be0ead..c3a6f5d 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -291,15 +291,24 @@
 
         // If the user has chosen provider, use that
         for (WebViewProviderInfo provider : providers) {
-            if (provider.packageName.equals(userChosenProvider)) {
+            if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
                 return provider.getPackageInfo();
             }
         }
 
-        // User did not choose, or the choice failed, use the most stable provider available
+        // User did not choose, or the choice failed; use the most stable provider that is
+        // enabled and available by default (not through user choice).
+        for (WebViewProviderInfo provider : providers) {
+            if (provider.isAvailableByDefault() && provider.isEnabled()) {
+                return provider.getPackageInfo();
+            }
+        }
+
+        // Could not find any enabled package either, use the most stable provider.
         for (WebViewProviderInfo provider : providers) {
             return provider.getPackageInfo();
         }
+
         mAnyWebViewInstalled = false;
         throw new WebViewFactory.MissingWebViewPackageException(
                 "Could not find a loadable WebView package");
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b49641f..46240783 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.StackId;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -24,12 +25,14 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.WINDOW_REPLACEMENT_TIMEOUT_DURATION;
 
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.wm.WindowManagerService.H;
 
 import android.annotation.NonNull;
 import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -38,6 +41,7 @@
 import android.view.WindowManager;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 
 class AppTokenList extends ArrayList<AppWindowToken> {
@@ -63,6 +67,7 @@
     // Whether we're performing an entering animation with a saved surface.
     boolean mAnimatingWithSavedSurface;
 
+
     Task mTask;
     boolean appFullscreen;
     int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -123,6 +128,10 @@
     // True if the windows associated with this token should be cropped to their stack bounds.
     boolean mCropWindowsToStack;
 
+    boolean mAlwaysFocusable;
+
+    ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
+
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
         super(_service, _token.asBinder(),
@@ -256,8 +265,8 @@
         return candidate;
     }
 
-    boolean stackCanReceiveKeys() {
-        return (windows.size() > 0) ? windows.get(windows.size() - 1).stackCanReceiveKeys() : false;
+    boolean windowsAreFocusable() {
+        return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable;
     }
 
     boolean isVisible() {
@@ -396,12 +405,28 @@
         }
     }
 
+    void resetReplacingWindows() {
+        if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Resetting app token " + appWindowToken
+                + " of replacing window marks.");
+
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.get(i);
+            w.resetReplacing();
+        }
+    }
+
     void addWindow(WindowState w) {
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             WindowState candidate = allAppWindows.get(i);
             if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
                     candidate.getWindowTag().equals(w.getWindowTag().toString())) {
                 candidate.mReplacingWindow = w;
+
+                // if we got a replacement window, reset the timeout to give drawing more time
+                service.mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+                service.mH.sendMessageDelayed(
+                        service.mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, this),
+                            WINDOW_REPLACEMENT_TIMEOUT_DURATION);
             }
         }
         allAppWindows.add(w);
@@ -417,7 +442,7 @@
         return false;
     }
 
-    void clearTimedoutReplaceesLocked() {
+    void clearTimedoutReplacesLocked() {
         for (int i = allAppWindows.size() - 1; i >= 0;
              // removeWindowLocked at bottom of loop may remove multiple entries from
              // allAppWindows if the window to be removed has child windows. It also may
@@ -430,10 +455,40 @@
                 continue;
             }
             candidate.mWillReplaceWindow = false;
-            service.removeWindowLocked(candidate);
+            // Since the window already timed out, remove it immediately now.
+            // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter
+            // delays removal on certain conditions, which will leave the stale window in the
+            // stack and marked mWillReplaceWindow=false, so the window will never be removed.
+            service.removeWindowInnerLocked(candidate);
         }
     }
 
+    /**
+     * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
+     * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
+     * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen
+     * with a queue.
+     */
+    void freezeBounds() {
+        mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds));
+    }
+
+    /**
+     * Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
+     */
+    void unfreezeBounds() {
+        mFrozenBounds.remove();
+        for (int i = windows.size() - 1; i >= 0; i--) {
+            final WindowState win = windows.get(i);
+            win.mLayoutNeeded = true;
+            win.setDisplayLayoutNeeded();
+            if (!service.mResizingWindows.contains(win)) {
+                service.mResizingWindows.add(win);
+            }
+        }
+        service.mWindowPlacerLocked.performSurfacePlacement();
+    }
+
     @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
@@ -480,6 +535,9 @@
                     pw.print(" startingDisplayed="); pw.print(startingDisplayed);
                     pw.print(" startingMoved"); pw.println(startingMoved);
         }
+        if (!mFrozenBounds.isEmpty()) {
+            pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5942198..0c429e5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -222,6 +222,10 @@
         out.set(left, top, left + width, top + height);
     }
 
+    void getContentRect(Rect out) {
+        out.set(mContentRect);
+    }
+
     /** Refer to {@link WindowManagerService#attachStack(int, int, boolean)} */
     void attachStack(TaskStack stack, boolean onTop) {
         if (stack.mStackId == HOME_STACK_ID) {
@@ -393,7 +397,7 @@
                     }
                     mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
                 }
-                if (task.isDockedInEffect() && !task.isResizeable()) {
+                if (task.isTwoFingerScrollMode()) {
                     stack.getBounds(mTmpRect);
                     mNonResizeableRegion.op(mTmpRect, Region.Op.UNION);
                     break;
@@ -570,38 +574,19 @@
             pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
             pw.print(subPrefix); pw.print("deferred="); pw.print(mDeferredRemoval);
                 pw.print(" layoutNeeded="); pw.println(layoutNeeded);
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
-            pw.print(prefix); pw.print("mStacks[" + stackNdx + "]"); pw.println(stack.mStackId);
-            stack.dump(prefix + "  ", pw);
-        }
+
         pw.println();
         pw.println("  Application tokens in top down Z order:");
-        int ndx = 0;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mStacks.get(stackNdx);
-            pw.print("  mStackId="); pw.println(stack.mStackId);
-            ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                pw.print("    mTaskId="); pw.println(task.mTaskId);
-                AppTokenList tokens = task.mAppTokens;
-                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) {
-                    final AppWindowToken wtoken = tokens.get(tokenNdx);
-                    pw.print("    Activity #"); pw.print(tokenNdx);
-                            pw.print(' '); pw.print(wtoken); pw.println(":");
-                    wtoken.dump(pw, "      ");
-                }
-            }
+            stack.dump(prefix + "  ", pw);
         }
-        if (ndx == 0) {
-            pw.println("    None");
-        }
+
         pw.println();
         if (!mExitingTokens.isEmpty()) {
             pw.println();
             pw.println("  Exiting tokens:");
-            for (int i=mExitingTokens.size()-1; i>=0; i--) {
+            for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
                 WindowToken token = mExitingTokens.get(i);
                 pw.print("  Exiting #"); pw.print(i);
                 pw.print(' '); pw.print(token);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8f3d3e3..7295318 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -18,11 +18,17 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.view.IDockDividerVisibilityListener;
+import android.view.DisplayInfo;
+import android.view.IDockedStackListener;
+import android.view.SurfaceControl;
+
+import com.android.server.wm.DimLayer.DimLayerUser;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
@@ -33,7 +39,7 @@
 /**
  * Keeps information about the docked stack divider.
  */
-public class DockedStackDividerController {
+public class DockedStackDividerController implements DimLayerUser {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
 
@@ -44,9 +50,10 @@
     private WindowState mWindow;
     private final Rect mTmpRect = new Rect();
     private final Rect mLastRect = new Rect();
-    private IDockDividerVisibilityListener mListener;
     private boolean mLastVisibility = false;
-    private boolean mForceVisibilityReevaluation;
+    private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
+            = new RemoteCallbackList<>();
+    private final DimLayer mDimLayer;
 
     DockedStackDividerController(Context context, DisplayContent displayContent) {
         mDisplayContent = displayContent;
@@ -54,6 +61,7 @@
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         mDividerInsets = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_insets);
+        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
     }
 
     boolean isResizing() {
@@ -83,15 +91,16 @@
             return;
         }
         mLastVisibility = visible;
-        if (mListener != null) {
-            try {
-                mListener.onDockDividerVisibilityChanged(visible);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "visibility call failed: " + e);
-            }
+        notifyDockedDividerVisibilityChanged(visible);
+        if (!visible) {
+            setResizeDimLayer(false, INVALID_STACK_ID, 0f);
         }
     }
 
+    boolean wasVisible() {
+        return mLastVisibility;
+    }
+
     void positionDockedStackedDivider(Rect frame) {
         TaskStack stack = mDisplayContent.getDockedStackLocked();
         if (stack == null) {
@@ -127,11 +136,76 @@
         mLastRect.set(frame);
     }
 
-    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
-        if (mListener != null && listener != null) {
-            throw new IllegalStateException("Dock divider visibility listener already set!");
+    void notifyDockedDividerVisibilityChanged(boolean visible) {
+        final int size = mDockedStackListeners.beginBroadcast();
+        for (int i = 0; i < size; ++i) {
+            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+            try {
+                listener.onDividerVisibilityChanged(visible);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
+            }
         }
-        mListener = listener;
-        reevaluateVisibility(true);
+        mDockedStackListeners.finishBroadcast();
+    }
+
+    void notifyDockedStackExistsChanged(boolean exists) {
+        final int size = mDockedStackListeners.beginBroadcast();
+        for (int i = 0; i < size; ++i) {
+            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+            try {
+                listener.onDockedStackExistsChanged(exists);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
+            }
+        }
+        mDockedStackListeners.finishBroadcast();
+    }
+
+    void registerDockedStackListener(IDockedStackListener listener) {
+        mDockedStackListeners.register(listener);
+        notifyDockedDividerVisibilityChanged(wasVisible());
+        notifyDockedStackExistsChanged(
+                mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
+    }
+
+    void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+        SurfaceControl.openTransaction();
+        TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
+        boolean visibleAndValid = visible && stack != null;
+        if (visibleAndValid) {
+            stack.getDimBounds(mTmpRect);
+            if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
+                mDimLayer.setBounds(mTmpRect);
+                mDimLayer.show(mDisplayContent.mService.mLayersController.getResizeDimLayer(),
+                        alpha, 0 /* duration */);
+            } else {
+                visibleAndValid = false;
+            }
+        }
+        if (!visibleAndValid) {
+            mDimLayer.hide();
+        }
+        SurfaceControl.closeTransaction();
+    }
+
+    @Override
+    public boolean isFullscreen() {
+        return false;
+    }
+
+    @Override
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayContent.getDisplayInfo();
+    }
+
+    @Override
+    public void getDimBounds(Rect outBounds) {
+        // This dim layer user doesn't need this.
+    }
+
+    @Override
+    public String toShortString() {
+        return TAG;
     }
 }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1b6957d..ac38424 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -203,15 +203,14 @@
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Configuration
-                    outConfig,
-            Surface outSurface) {
+            Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+            Configuration outConfig, Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outStableInsets, outsets, outConfig, outSurface);
+                outStableInsets, outsets, outBackdropFrame, outConfig, outSurface);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
         return res;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 27e7a31..d9667a1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -61,6 +61,13 @@
 
     // Content limits relative to the DisplayContent this sits in.
     private Rect mBounds = new Rect();
+    final Rect mPreparedFrozenBounds = new Rect();
+
+    private Rect mPreScrollBounds = new Rect();
+    private boolean mScrollValid;
+
+    // Bounds used to calculate the insets.
+    private final Rect mTempInsetBounds = new Rect();
 
     // Device rotation as of the last time {@link #mBounds} was set.
     int mRotation;
@@ -123,12 +130,25 @@
         int yOffset = 0;
         if (dockSide != DOCKED_INVALID) {
             mStack.getBounds(mTmpRect);
-            displayContent.getLogicalDisplayRect(mTmpRect2);
 
             if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
+                // The toast was originally placed at the bottom and centered. To place it
+                // at the bottom-center of the stack, we offset it horizontally by the diff
+                // between the center of the stack bounds vs. the center of the screen.
+                displayContent.getLogicalDisplayRect(mTmpRect2);
                 xOffset = mTmpRect.centerX() - mTmpRect2.centerX();
             } else if (dockSide == DOCKED_TOP) {
+                // The toast was originally placed at the bottom and centered. To place it
+                // at the bottom center of the top stack, we offset it vertically by the diff
+                // between the bottom of the stack bounds vs. the bottom of the content rect.
+                //
+                // Note here we use the content rect instead of the display rect, as we want
+                // the toast's distance to the dock divider (when it's placed at the top half)
+                // to be the same as it's distance to the top of the navigation bar (when it's
+                // placed at the bottom).
+
                 // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom.
+                displayContent.getContentRect(mTmpRect2);
                 yOffset = mTmpRect2.bottom - mTmpRect.bottom;
             }
             mService.mH.obtainMessage(
@@ -153,8 +173,17 @@
         mDeferRemoval = false;
     }
 
+    private boolean hasAppTokensAlive() {
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            if (!mAppTokens.get(i).appDied) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void removeLocked() {
-        if (!mAppTokens.isEmpty() && mStack.isAnimating()) {
+        if (hasAppTokensAlive() && mStack.isAnimating()) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: deferring removing taskId=" + mTaskId);
             mDeferRemoval = true;
             return;
@@ -197,8 +226,7 @@
     boolean removeAppToken(AppWindowToken wtoken) {
         boolean removed = mAppTokens.remove(wtoken);
         if (mAppTokens.size() == 0) {
-            EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId,
-                    "removeAppToken: last token");
+            EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
             if (mDeferRemoval) {
                 removeLocked();
             }
@@ -246,19 +274,23 @@
             // Can't set to fullscreen if we don't have a display to get bounds from...
             return BOUNDS_CHANGE_NONE;
         }
-        if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+        if (mPreScrollBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
             return BOUNDS_CHANGE_NONE;
         }
 
         int boundsChange = BOUNDS_CHANGE_NONE;
-        if (mBounds.left != bounds.left || mBounds.top != bounds.top) {
+        if (mPreScrollBounds.left != bounds.left || mPreScrollBounds.top != bounds.top) {
             boundsChange |= BOUNDS_CHANGE_POSITION;
         }
-        if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) {
+        if (mPreScrollBounds.width() != bounds.width() || mPreScrollBounds.height() != bounds.height()) {
             boundsChange |= BOUNDS_CHANGE_SIZE;
         }
 
-        mBounds.set(bounds);
+
+        mPreScrollBounds.set(bounds);
+
+        resetScrollLocked();
+
         mRotation = rotation;
         if (displayContent != null) {
             displayContent.mDimLayerController.updateDimLayer(this);
@@ -267,6 +299,26 @@
         return boundsChange;
     }
 
+    /**
+     * Sets the bounds used to calculate the insets. See
+     * {@link android.app.IActivityManager#resizeDockedStack} why this is needed.
+     */
+    void setTempInsetBounds(Rect tempInsetBounds) {
+        if (tempInsetBounds != null) {
+            mTempInsetBounds.set(tempInsetBounds);
+        } else {
+            mTempInsetBounds.setEmpty();
+        }
+    }
+
+    /**
+     * Gets the bounds used to calculate the insets. See
+     * {@link android.app.IActivityManager#resizeDockedStack} why this is needed.
+     */
+    void getTempInsetBounds(Rect out) {
+        out.set(mTempInsetBounds);
+    }
+
     void setResizeable(boolean resizeable) {
         mResizeable = resizeable;
     }
@@ -291,6 +343,40 @@
         return true;
     }
 
+    /**
+     * Prepares the task bounds to be frozen with the current size. See
+     * {@link AppWindowToken#freezeBounds}.
+     */
+    void prepareFreezingBounds() {
+        mPreparedFrozenBounds.set(mBounds);
+    }
+
+    void resetScrollLocked() {
+        if (mScrollValid) {
+            mScrollValid = false;
+            applyScrollToAllWindows(0, 0);
+        }
+        mBounds.set(mPreScrollBounds);
+    }
+
+    void applyScrollToAllWindows(final int xOffset, final int yOffset) {
+        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState win = windows.get(winNdx);
+                win.mXOffset = xOffset;
+                win.mYOffset = yOffset;
+            }
+        }
+    }
+
+    void applyScrollToWindowIfNeeded(final WindowState win) {
+        if (mScrollValid) {
+            win.mXOffset = mBounds.left;
+            win.mYOffset = mBounds.top;
+        }
+    }
+
     boolean scrollLocked(Rect bounds) {
         // shift the task bound if it doesn't fully cover the stack area
         mStack.getDimBounds(mTmpRect);
@@ -312,21 +398,17 @@
             }
         }
 
-        if (bounds.equals(mBounds)) {
+        // We can stop here if we're already scrolling and the scrolled bounds not changed.
+        if (mScrollValid && bounds.equals(mBounds)) {
             return false;
         }
+
         // Normal setBounds() does not allow non-null bounds for fullscreen apps.
         // We only change bounds for the scrolling case without change it size,
         // on resizing path we should still want the validation.
         mBounds.set(bounds);
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState win = windows.get(winNdx);
-                win.mXOffset = bounds.left;
-                win.mYOffset = bounds.top;
-            }
-        }
+        mScrollValid = true;
+        applyScrollToAllWindows(bounds.left, bounds.top);
         return true;
     }
 
@@ -343,7 +425,7 @@
     }
 
     /** Original bounds of the task if applicable, otherwise fullscreen rect. */
-    public void getBounds(Rect out) {
+    void getBounds(Rect out) {
         if (useCurrentBounds()) {
             // No need to adjust the output bounds if fullscreen or the docked stack is visible
             // since it is already what we want to represent to the rest of the system.
@@ -351,13 +433,11 @@
             return;
         }
 
-        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
-        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
-        // system.
+        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is
+        // not currently visible. Go ahead a represent it as fullscreen to the rest of the system.
         mStack.getDisplayContent().getLogicalDisplayRect(out);
     }
 
-
     /**
      * Calculate the maximum visible area of this task. If the task has only one app,
      * the result will be visible frame of that app. If the task has more than one apps,
@@ -442,19 +522,25 @@
             return;
         }
 
-        // Device rotation changed. We don't want the task to move around on the screen when
-        // this happens, so update the task bounds so it stays in the same place.
-        mTmpRect2.set(mBounds);
+        // Device rotation changed.
+        // - Reset the bounds to the pre-scroll bounds as whatever scrolling was done is no longer
+        // valid.
+        // - Rotate the bounds and notify activity manager if the task can be resized independently
+        // from its stack. The stack will take care of task rotation for the other case.
+        mTmpRect2.set(mPreScrollBounds);
+
+        if (!StackId.isTaskResizeAllowed(mStack.mStackId)) {
+            setBounds(mTmpRect2, mOverrideConfig);
+            return;
+        }
+
         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
-            // Post message to inform activity manager of the bounds change simulating
-            // a one-way call. We do this to prevent a deadlock between window manager
-            // lock and activity manager lock been held. Only tasks within the freeform stack
-            // are resizeable independently of their stack resizing.
-            if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
-                mService.mH.sendMessage(mService.mH.obtainMessage(
-                        RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
-            }
+            // Post message to inform activity manager of the bounds change simulating a one-way
+            // call. We do this to prevent a deadlock between window manager lock and activity
+            // manager lock been held.
+            mService.mH.obtainMessage(RESIZE_TASK, mTaskId,
+                    RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mPreScrollBounds).sendToTarget();
         }
     }
 
@@ -519,8 +605,9 @@
     }
 
     boolean isResizeableByDockedStack() {
-        return mStack != null && getDisplayContent().getDockedStackLocked() != null &&
-                StackId.isTaskResizeableByDockedStack(mStack.mStackId);
+        final DisplayContent displayContent = getDisplayContent();
+        return displayContent != null && displayContent.getDockedStackLocked() != null
+                && mStack != null && StackId.isTaskResizeableByDockedStack(mStack.mStackId);
     }
 
     /**
@@ -531,6 +618,10 @@
         return inDockedWorkspace() || isResizeableByDockedStack();
     }
 
+    boolean isTwoFingerScrollMode() {
+        return isDockedInEffect() && !isResizeable();
+    }
+
     WindowState getTopVisibleAppMainWindow() {
         final AppWindowToken token = getTopVisibleAppToken();
         return token != null ? token.findMainWindow() : null;
@@ -573,11 +664,23 @@
         return "Task=" + mTaskId;
     }
 
-    public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("taskId="); pw.println(mTaskId);
-            pw.print(prefix + prefix); pw.print("mFullscreen="); pw.println(mFullscreen);
-            pw.print(prefix + prefix); pw.print("mBounds="); pw.println(mBounds.toShortString());
-            pw.print(prefix + prefix); pw.print("mdr="); pw.println(mDeferRemoval);
-            pw.print(prefix + prefix); pw.print("appTokens="); pw.println(mAppTokens);
+    public void dump(String prefix, PrintWriter pw) {
+        final String doublePrefix = prefix + "  ";
+
+        pw.println(prefix + "taskId=" + mTaskId);
+        pw.println(doublePrefix + "mFullscreen=" + mFullscreen);
+        pw.println(doublePrefix + "mBounds=" + mBounds.toShortString());
+        pw.println(doublePrefix + "mdr=" + mDeferRemoval);
+        pw.println(doublePrefix + "appTokens=" + mAppTokens);
+        pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
+
+        final String triplePrefix = doublePrefix + "  ";
+
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            final AppWindowToken wtoken = mAppTokens.get(i);
+            pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
+            wtoken.dump(pw, triplePrefix);
+        }
+
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b961879..a75f2c9 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -26,6 +26,9 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -34,6 +37,7 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
@@ -85,6 +89,7 @@
 
     /** Detach this stack from its display when animation completes. */
     boolean mDeferDetach;
+    private boolean mUpdateBoundsAfterRotation = false;
 
     TaskStack(WindowManagerService service, int stackId) {
         mService = service;
@@ -100,12 +105,6 @@
         return mTasks;
     }
 
-    void resizeWindows() {
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTasks.get(taskNdx).resizeWindows();
-        }
-    }
-
     /**
      * Set the bounds of the stack and its containing tasks.
      * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
@@ -114,7 +113,8 @@
      * @return True if the stack bounds was changed.
      * */
     boolean setBounds(
-            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
+            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
+            SparseArray<Rect> taskTempInsetBounds) {
         if (!setBounds(stackBounds)) {
             return false;
         }
@@ -125,17 +125,20 @@
             Configuration config = configs.get(task.mTaskId);
             if (config != null) {
                 Rect bounds = taskBounds.get(task.mTaskId);
-                if (!task.isResizeable() && task.isDockedInEffect()) {
+                if (task.isTwoFingerScrollMode()) {
                     // This is a non-resizeable task that's docked (or side-by-side to the docked
                     // stack). It might have been scrolled previously, and after the stack resizing,
                     // it might no longer fully cover the stack area.
                     // Save the old bounds and re-apply the scroll. This adjusts the bounds to
                     // fit the new stack bounds.
+                    task.resizeLocked(bounds, config, false /* forced */);
                     task.getBounds(mTmpRect);
-                    task.setBounds(bounds, config);
                     task.scrollLocked(mTmpRect);
                 } else {
-                    task.setBounds(bounds, config);
+                    task.resizeLocked(bounds, config, false /* forced */);
+                    task.setTempInsetBounds(
+                            taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId)
+                                    : null);
                 }
             } else {
                 Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?");
@@ -144,6 +147,13 @@
         return true;
     }
 
+    void prepareFreezingTaskBounds() {
+        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mTasks.get(taskNdx);
+            task.prepareFreezingBounds();
+        }
+    }
+
     boolean isFullscreenBounds(Rect bounds) {
         if (mDisplayContent == null || bounds == null) {
             return true;
@@ -224,6 +234,7 @@
     }
 
     void updateDisplayInfo(Rect bounds) {
+        mUpdateBoundsAfterRotation = false;
         if (mDisplayContent != null) {
             for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
@@ -233,20 +244,72 @@
             } else if (mFullscreen) {
                 setBounds(null);
             } else {
+                mUpdateBoundsAfterRotation = true;
                 mTmpRect2.set(mBounds);
-                mDisplayContent.rotateBounds(
-                        mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
-                if (setBounds(mTmpRect2)) {
-                    // Post message to inform activity manager of the bounds change simulating
-                    // a one-way call. We do this to prevent a deadlock between window manager
-                    // lock and activity manager lock been held.
-                    mService.mH.sendMessage(mService.mH.obtainMessage(
-                            RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mBounds));
+                final int newRotation = mDisplayContent.getDisplayInfo().rotation;
+                if (mRotation == newRotation) {
+                    setBounds(mTmpRect2);
                 }
+
+                // If the rotation changes, we'll handle it in updateBoundsAfterRotation
             }
         }
     }
 
+    /**
+     * Updates the bounds after rotating the screen. We can't handle it in
+     * {@link #updateDisplayInfo} because at that point the configuration might not be fully updated
+     * yet.
+     */
+    void updateBoundsAfterRotation() {
+        if (!mUpdateBoundsAfterRotation) {
+            return;
+        }
+        mUpdateBoundsAfterRotation = false;
+        final int newRotation = getDisplayInfo().rotation;
+        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
+        if (mStackId == DOCKED_STACK_ID) {
+            snapDockedStackAfterRotation(mTmpRect2);
+        }
+
+        // Post message to inform activity manager of the bounds change simulating
+        // a one-way call. We do this to prevent a deadlock between window manager
+        // lock and activity manager lock been held.
+        mService.mH.sendMessage(mService.mH.obtainMessage(
+                RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2));
+    }
+
+    /**
+     * Snaps the bounds after rotation to the closest snap target for the docked stack.
+     */
+    private void snapDockedStackAfterRotation(Rect outBounds) {
+
+        // Calculate the current position.
+        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+        final int dividerSize = mService.getDefaultDisplayContentLocked()
+                .getDockedDividerController().getContentWidth();
+        final int dockSide = getDockSide(outBounds);
+        final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
+                dockSide, dividerSize);
+        final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
+        final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
+
+        // Snap the position to a target.
+        final int rotation = displayInfo.rotation;
+        final int orientation = mService.mCurConfiguration.orientation;
+        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
+        final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
+                mService.mContext.getResources(),
+                0 /* minFlingVelocityPxPerSecond */, displayWidth, displayHeight,
+                dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
+        final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
+
+        // Recalculate the bounds based on the position of the target.
+        DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
+                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
+                dividerSize);
+    }
+
     boolean isAnimating() {
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
@@ -297,6 +360,10 @@
                 "positionTask: task=" + task + " position=" + position);
         mTasks.add(position, task);
 
+        // If we are moving the task across stacks, the scroll is no longer valid.
+        if (task.mStack != this) {
+            task.resetScrollLocked();
+        }
         task.mStack = this;
         task.updateDisplayInfo(mDisplayContent);
         boolean toTop = position == mTasks.size() - 1;
@@ -418,7 +485,7 @@
         }
     }
 
-    void getStackDockedModeBoundsLocked(Rect outBounds) {
+    void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibilityOnKeyguardShowing) {
         if (!StackId.isResizeableByDockedStack(mStackId) || mDisplayContent == null) {
             outBounds.set(mBounds);
             return;
@@ -430,11 +497,11 @@
             throw new IllegalStateException(
                     "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
         }
-        if (!dockedStack.isVisibleLocked()) {
+        if (!dockedStack.isVisibleLocked(ignoreVisibilityOnKeyguardShowing)) {
             // The docked stack is being dismissed, but we caught before it finished being
             // dismissed. In that case we want to treat it as if it is not occupying any space and
             // let others occupy the whole display.
-            mDisplayContent.getLogicalDisplayRect(mTmpRect);
+            mDisplayContent.getLogicalDisplayRect(outBounds);
             return;
         }
 
@@ -477,20 +544,32 @@
                 outBounds.set(mService.mDockedStackCreateBounds);
                 return;
             }
-            // The initial bounds of the docked stack when it is created half the screen space and
-            // its bounds can be adjusted after that. The bounds of all other stacks are adjusted
-            // to occupy whatever screen space the docked stack isn't occupying.
+
+            // The initial bounds of the docked stack when it is created about half the screen space
+            // and its bounds can be adjusted after that. The bounds of all other stacks are
+            // adjusted to occupy whatever screen space the docked stack isn't occupying.
+            final DisplayInfo di = mDisplayContent.getDisplayInfo();
+            mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+                    mTmpRect2);
+            final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
+                    0 /* minFlingVelocityPxPerSecond */,
+                    di.logicalWidth,
+                    di.logicalHeight,
+                    dockDividerWidth,
+                    mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT,
+                    mTmpRect2).getMiddleTarget().position;
+
             if (dockOnTopOrLeft) {
                 if (splitHorizontally) {
-                    outBounds.right = displayRect.centerX() - dockDividerWidth / 2;
+                    outBounds.right = position;
                 } else {
-                    outBounds.bottom = displayRect.centerY() - dockDividerWidth / 2;
+                    outBounds.bottom = position;
                 }
             } else {
                 if (splitHorizontally) {
-                    outBounds.left = displayRect.centerX() + dockDividerWidth / 2;
+                    outBounds.left = position - dockDividerWidth;
                 } else {
-                    outBounds.top = displayRect.centerY() + dockDividerWidth / 2;
+                    outBounds.top = position - dockDividerWidth;
                 }
             }
             return;
@@ -608,16 +687,15 @@
     }
 
     public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
-        pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
-        pw.print(prefix); pw.print("mFullscreen="); pw.println(mFullscreen);
-        pw.print(prefix); pw.print("mBounds="); pw.println(mBounds.toShortString());
-        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
-            pw.print(prefix);
-            mTasks.get(taskNdx).printTo(prefix + " ", pw);
+        pw.println(prefix + "mStackId=" + mStackId);
+        pw.println(prefix + "mDeferDetach=" + mDeferDetach);
+        pw.println(prefix + "mFullscreen=" + mFullscreen);
+        pw.println(prefix + "mBounds=" + mBounds.toShortString());
+        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
+            mTasks.get(taskNdx).dump(prefix + "  ", pw);
         }
         if (mAnimationBackgroundSurface.isDimming()) {
-            pw.print(prefix); pw.println("mWindowAnimationBackgroundSurface:");
+            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
             mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
         }
         if (!mExitingAppTokens.isEmpty()) {
@@ -672,6 +750,10 @@
      * information which side of the screen was the dock anchored.
      */
     int getDockSide() {
+        return getDockSide(mBounds);
+    }
+
+    int getDockSide(Rect bounds) {
         if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
             return DOCKED_INVALID;
         }
@@ -682,14 +764,14 @@
         final int orientation = mService.mCurConfiguration.orientation;
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             // Portrait mode, docked either at the top or the bottom.
-            if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
+            if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) {
                 return DOCKED_TOP;
             } else {
                 return DOCKED_BOTTOM;
             }
         } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             // Landscape mode, docked either on the left or on the right.
-            if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
+            if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) {
                 return DOCKED_LEFT;
             } else {
                 return DOCKED_RIGHT;
@@ -700,10 +782,19 @@
     }
 
     boolean isVisibleLocked() {
+        return isVisibleLocked(false);
+    }
+
+    boolean isVisibleLocked(boolean ignoreVisibilityOnKeyguardShowing) {
         final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded();
         if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
-            return false;
+            // The keyguard is showing and the stack shouldn't show on top of the keyguard.
+            // We return false for visibility except in cases where the caller wants us to return
+            // true for visibility when the keyguard is showing. One example, is if the docked
+            // is being resized due to orientation while the keyguard is on.
+            return ignoreVisibilityOnKeyguardShowing;
         }
+
         for (int i = mTasks.size() - 1; i >= 0; i--) {
             Task task = mTasks.get(i);
             for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 98033f6..3dc512f 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -34,13 +34,7 @@
 import static android.view.PointerIcon.STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
 
 public class TaskTapPointerEventListener implements PointerEventListener {
-    private static final int TAP_TIMEOUT_MSEC = 300;
-    private static final float TAP_MOTION_SLOP_INCHES = 0.125f;
 
-    private final int mMotionSlop;
-    private float mDownX;
-    private float mDownY;
-    private int mPointerId;
     final private Region mTouchExcludeRegion = new Region();
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
@@ -55,8 +49,6 @@
             DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
-        DisplayInfo info = displayContent.getDisplayInfo();
-        mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
     }
 
     // initialize the object, note this must be done outside WindowManagerService
@@ -74,31 +66,19 @@
         final int action = motionEvent.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
-                mPointerId = motionEvent.getPointerId(0);
-                mDownX = motionEvent.getX();
-                mDownY = motionEvent.getY();
+                final int x = (int) motionEvent.getX();
+                final int y = (int) motionEvent.getY();
 
-                final int x = (int) mDownX;
-                final int y = (int) mDownY;
                 synchronized (this) {
                     if (!mTouchExcludeRegion.contains(x, y)) {
-                        mService.mH.obtainMessage(H.TAP_DOWN_OUTSIDE_TASK, x, y,
-                                mDisplayContent).sendToTarget();
+                        mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK,
+                                x, y, mDisplayContent).sendToTarget();
                     }
                 }
                 break;
             }
 
             case MotionEvent.ACTION_MOVE: {
-                if (mPointerId >= 0) {
-                    int index = motionEvent.findPointerIndex(mPointerId);
-                    if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
-                            || index < 0
-                            || Math.abs(motionEvent.getX(index) - mDownX) > mMotionSlop
-                            || Math.abs(motionEvent.getY(index) - mDownY) > mMotionSlop) {
-                        mPointerId = -1;
-                    }
-                }
                 if (motionEvent.getPointerCount() != 2) {
                     stopTwoFingerScroll();
                 }
@@ -149,24 +129,6 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_POINTER_UP: {
-                int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-                // Extract the index of the pointer that left the touch sensor
-                if (mPointerId == motionEvent.getPointerId(index)) {
-                    final int x = (int)motionEvent.getX(index);
-                    final int y = (int)motionEvent.getY(index);
-                    synchronized(this) {
-                        if ((motionEvent.getEventTime() - motionEvent.getDownTime())
-                                < TAP_TIMEOUT_MSEC
-                                && Math.abs(x - mDownX) < mMotionSlop
-                                && Math.abs(y - mDownY) < mMotionSlop
-                                && !mTouchExcludeRegion.contains(x, y)) {
-                            mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK, x, y,
-                                    mDisplayContent).sendToTarget();
-                        }
-                    }
-                    mPointerId = -1;
-                }
                 stopTwoFingerScroll();
                 break;
             }
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 263b411..2cf2618 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -1,9 +1,20 @@
-package com.android.server.wm;
+/*
+ * Copyright (C) 2015 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.
+ */
 
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
+package com.android.server.wm;
 
 import android.app.ActivityManager.StackId;
 import android.util.Slog;
@@ -11,6 +22,11 @@
 
 import java.io.PrintWriter;
 
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
+
 /**
  * Controller for assigning layers to windows on the display.
  *
@@ -42,7 +58,6 @@
     private WindowState mPinnedWindow = null;
     private WindowState mDockedWindow = null;
     private WindowState mDockDivider = null;
-    private WindowState mImeWindow = null;
     private WindowState mReplacingWindow = null;
 
     final void assignLayersLocked(WindowList windows) {
@@ -133,6 +148,14 @@
         return 0;
     }
 
+    /**
+     * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
+     *         above all application surfaces.
+     */
+    int getResizeDimLayer() {
+        return mDockDivider.mLayer - 1;
+    }
+
     private void logDebugLayers(WindowList windows) {
         for (int i = 0, n = windows.size(); i < n; i++) {
             final WindowState w = windows.get(i);
@@ -146,16 +169,13 @@
 
     private void clear() {
         mHighestApplicationLayer = 0;
-        mImeWindow = null;
         mPinnedWindow = null;
         mDockedWindow = null;
         mDockDivider = null;
     }
 
     private void collectSpecialWindows(WindowState w) {
-        if (w.mIsImWindow) {
-            mImeWindow = w;
-        } else if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
+        if (w.mAttrs.type == TYPE_DOCK_DIVIDER) {
             mDockDivider = w;
         } else {
             final TaskStack stack = w.getStack();
@@ -175,18 +195,17 @@
         // For pinned and docked stack window, we want to make them above other windows
         // also when these windows are animating.
         layer = assignAndIncreaseLayerIfNeeded(mDockedWindow, layer);
+
+        // Leave some space here so the dim layer while dismissing docked/fullscreen stack has space
+        // below the divider but above the app windows. It needs to be below the divider in because
+        // the divider sometimes overlaps the app windows.
+        layer++;
         layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
         // We know that we will be animating a relaunching window in the near future,
         // which will receive a z-order increase. We want the replaced window to
         // immediately receive the same treatment, e.g. to be above the dock divider.
         layer = assignAndIncreaseLayerIfNeeded(mReplacingWindow, layer);
         layer = assignAndIncreaseLayerIfNeeded(mPinnedWindow, layer);
-        final WindowState inputMethodTarget = mService.mInputMethodTarget;
-        // There might be no applications windows yet, so we need to make sure we uplift the input
-        // method above the target.
-        layer = Math.max(layer,
-                inputMethodTarget != null ? inputMethodTarget.mWinAnimator.mAnimLayer + 1 : 0);
-        layer = assignAndIncreaseLayerIfNeeded(mImeWindow, layer);
     }
 
     private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 845100d..4c3a422 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -35,7 +35,7 @@
 
     static final boolean DEBUG_RESIZE = false;
     static final boolean DEBUG = false;
-    static final boolean DEBUG_ADD_REMOVE = false;
+    static final boolean DEBUG_ADD_REMOVE = true;
     static final boolean DEBUG_FOCUS = true;
     static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
     static final boolean DEBUG_ANIM = false;
@@ -50,8 +50,8 @@
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
-    static final boolean DEBUG_APP_TRANSITIONS = false;
-    static final boolean DEBUG_STARTING_WINDOW = false;
+    static final boolean DEBUG_APP_TRANSITIONS = true;
+    static final boolean DEBUG_STARTING_WINDOW = true;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
     static final boolean DEBUG_DRAG = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6385caa..adc2da4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,146 @@
 
 package com.android.server.wm;
 
+import android.Manifest;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager.StackId;
+import android.app.ActivityManagerNative;
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.SystemService;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.TypedValue;
+import android.view.AppTransitionAnimationSpec;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.IApplicationToken;
+import android.view.IDockedStackListener;
+import android.view.IInputFilter;
+import android.view.IOnKeyguardExitResult;
+import android.view.IRotationWatcher;
+import android.view.IWindow;
+import android.view.IWindowId;
+import android.view.IWindowManager;
+import android.view.IWindowSession;
+import android.view.IWindowSessionCallback;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.MagnificationSpec;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowContentFrameStats;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerInternal;
+import android.view.WindowManagerPolicy;
+import android.view.WindowManagerPolicy.PointerEventListener;
+import android.view.animation.Animation;
+import android.view.inputmethod.InputMethodManagerInternal;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodClient;
+import com.android.internal.view.IInputMethodManager;
+import com.android.internal.view.WindowManagerPolicyThread;
+import com.android.server.AttributeCache;
+import com.android.server.DisplayThread;
+import com.android.server.EventLogTags;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.Watchdog;
+import com.android.server.input.InputManagerService;
+import com.android.server.policy.PhoneWindowManager;
+import com.android.server.power.ShutdownThread;
+
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.Socket;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -85,148 +225,14 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.Manifest;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManagerNative;
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerInternal;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.PowerManagerInternal;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.StrictMode;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.SystemService;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.util.ArraySet;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.TypedValue;
-import android.view.AppTransitionAnimationSpec;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.Gravity;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.IDockDividerVisibilityListener;
-import android.view.IInputFilter;
-import android.view.IOnKeyguardExitResult;
-import android.view.IRotationWatcher;
-import android.view.IWindow;
-import android.view.IWindowId;
-import android.view.IWindowManager;
-import android.view.IWindowSession;
-import android.view.IWindowSessionCallback;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MagnificationSpec;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.View;
-import android.view.WindowContentFrameStats;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.WindowManagerGlobal;
-import android.view.WindowManagerInternal;
-import android.view.WindowManagerPolicy;
-import android.view.WindowManagerPolicy.PointerEventListener;
-import android.view.animation.Animation;
-import android.view.inputmethod.InputMethodManagerInternal;
-import android.widget.Toast;
-
-import com.android.internal.R;
-import com.android.internal.app.IAssistScreenshotReceiver;
-import com.android.internal.util.FastPrintWriter;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
-import com.android.internal.view.WindowManagerPolicyThread;
-import com.android.server.AttributeCache;
-import com.android.server.DisplayThread;
-import com.android.server.EventLogTags;
-import com.android.server.FgThread;
-import com.android.server.LocalServices;
-import com.android.server.UiThread;
-import com.android.server.Watchdog;
-import com.android.server.input.InputManagerService;
-import com.android.server.policy.PhoneWindowManager;
-import com.android.server.power.ShutdownThread;
-
-import java.io.BufferedWriter;
-import java.io.DataInputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.Socket;
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
+
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
     static final boolean PROFILE_ORIENTATION = false;
@@ -573,31 +579,23 @@
     final ArrayList<WindowState> mTmpWindows = new ArrayList<>();
 
     boolean mHardKeyboardAvailable;
-    boolean mShowImeWithHardKeyboard;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
     SettingsObserver mSettingsObserver;
 
     private final class SettingsObserver extends ContentObserver {
-        private final Uri mShowImeWithHardKeyboardUri =
-                Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
-
         private final Uri mDisplayInversionEnabledUri =
                 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
 
         public SettingsObserver() {
             super(new Handler());
             ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(mShowImeWithHardKeyboardUri, false, this,
-                    UserHandle.USER_ALL);
             resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this,
                     UserHandle.USER_ALL);
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            if (mShowImeWithHardKeyboardUri.equals(uri)) {
-                updateShowImeWithHardKeyboard();
-            } else if (mDisplayInversionEnabledUri.equals(uri)) {
+            if (mDisplayInversionEnabledUri.equals(uri)) {
                 updateCircularDisplayMaskIfNeeded();
             }
         }
@@ -941,7 +939,6 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         mSettingsObserver = new SettingsObserver();
-        updateShowImeWithHardKeyboard();
 
         mHoldingScreenWakeLock = mPowerManager.newWakeLock(
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
@@ -1994,6 +1991,10 @@
                 }
             }
 
+            // If the window is being added to a task that's docked but non-resizeable,
+            // we need to update this new window's scroll position when it's added.
+            win.applyScrollIfNeeded();
+
             if (type == TYPE_DOCK_DIVIDER) {
                 getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win);
             }
@@ -2233,9 +2234,8 @@
             // trigger its removal.
             final boolean lastWinStartingNotAnimating = startingWindow && appToken!= null
                     && appToken.allAppWindows.size() == 1 && !isAnimating;
-            if (!lastWinStartingNotAnimating && (win.mExiting || isAnimating)) {
+            if (!lastWinStartingNotAnimating && win.mExiting) {
                 // The exit animation is running... wait for it!
-                win.mExiting = true;
                 win.mRemoveOnExit = true;
                 win.setDisplayLayoutNeeded();
                 final boolean focusChanged = updateFocusedWindowLocked(
@@ -2426,13 +2426,17 @@
         }
     }
 
-    void setInsetsWindow(Session session, IWindow client,
-            int touchableInsets, Rect contentInsets,
+    void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
             Rect visibleInsets, Region touchableRegion) {
         long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mWindowMap) {
                 WindowState w = windowForClientLocked(session, client, false);
+                if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w
+                        + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets
+                        + ", visibleInsets=" + w.mGivenVisibleInsets + " -> " + visibleInsets
+                        + ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion
+                        + ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets);
                 if (w != null) {
                     w.mGivenInsetsPending = false;
                     w.mGivenContentInsets.set(contentInsets);
@@ -2557,8 +2561,8 @@
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Configuration outConfig,
-            Surface outSurface) {
+            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
+            Configuration outConfig, Surface outSurface) {
         int result = 0;
         boolean configChanged;
         boolean hasStatusBarPermission =
@@ -2745,6 +2749,7 @@
             outVisibleInsets.set(win.mVisibleInsets);
             outStableInsets.set(win.mStableInsets);
             outOutsets.set(win.mOutsets);
+            outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
             if (localLOGV) Slog.v(
                 TAG_WM, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
@@ -2865,7 +2870,12 @@
                 result |= WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
             }
         }
-        result |= win.isDragResizing() ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING : 0;
+        final boolean freeformResizing = win.isDragResizing()
+                && win.getResizeMode() == WindowState.DRAG_RESIZE_MODE_FREEFORM;
+        final boolean dockedResizing = win.isDragResizing()
+                && win.getResizeMode() == WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+        result |= freeformResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0;
+        result |= dockedResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0;
         if (win.isAnimatingWithSavedSurface()) {
             // If we're animating with a saved surface now, request client to report draw.
             // We still need to know when the real thing is drawn.
@@ -2978,7 +2988,7 @@
                     + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
             Animation a = mAppTransition.loadAnimation(lp, transit, enter,
                     mCurConfiguration.orientation, frame, insets, surfaceInsets, isVoiceInteraction,
-                    freeform, atoken.mTask.mTaskId);
+                    !fullscreen, atoken.mTask.mTaskId);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -3203,7 +3213,8 @@
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
             int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
             int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
-            Rect taskBounds, Configuration config, boolean cropWindowsToStack) {
+            Rect taskBounds, Configuration config, boolean cropWindowsToStack,
+            boolean alwaysFocusable) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3238,6 +3249,7 @@
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
             atoken.mLaunchTaskBehind = launchTaskBehind;
             atoken.mCropWindowsToStack = cropWindowsToStack;
+            atoken.mAlwaysFocusable = alwaysFocusable;
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
                     + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
 
@@ -3467,6 +3479,7 @@
             // the value of the previous configuration.
             mTempConfiguration.setToDefaults();
             mTempConfiguration.fontScale = currentConfig.fontScale;
+            mTempConfiguration.uiMode = currentConfig.uiMode;
             computeScreenConfigurationLocked(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
                 mWaitingForConfig = true;
@@ -3521,6 +3534,7 @@
         }
     }
 
+    @Override
     public void setNewConfiguration(Configuration config) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setNewConfiguration()")) {
@@ -3534,10 +3548,20 @@
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
+            if (orientationChanged) {
+                updateTaskStackBoundsAfterRotation();
+            }
             mWindowPlacerLocked.performSurfacePlacement();
         }
     }
 
+    private void updateTaskStackBoundsAfterRotation() {
+        for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
+            final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
+            stack.updateBoundsAfterRotation();
+        }
+    }
+
     @Override
     public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -3877,7 +3901,7 @@
             return false;
         }
         WindowState startingWindow = ttoken.startingWindow;
-        if (startingWindow != null) {
+        if (startingWindow != null && ttoken.startingView != null) {
             // In this case, the starting icon has already been displayed, so start
             // letting windows get shown immediately without any more transitions.
             mSkipAppTransitionAnimation = true;
@@ -4651,6 +4675,10 @@
                         if (DEBUG_STACK) Slog.d(TAG_WM, "attachStack: stackId=" + stackId);
                         stack = new TaskStack(this, stackId);
                         mStackIdToStack.put(stackId, stack);
+                        if (stackId == DOCKED_STACK_ID) {
+                            getDefaultDisplayContentLocked().mDividerControllerLocked
+                                    .notifyDockedStackExistsChanged(true);
+                        }
                     }
                     stack.attachDisplayContent(displayContent);
                     displayContent.attachStack(stack, onTop);
@@ -4676,6 +4704,10 @@
     void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
         displayContent.detachStack(stack);
         stack.detachDisplay();
+        if (stack.mStackId == DOCKED_STACK_ID) {
+            getDefaultDisplayContentLocked().mDividerControllerLocked
+                    .notifyDockedStackExistsChanged(false);
+        }
     }
 
     public void detachStack(int stackId) {
@@ -4769,11 +4801,12 @@
         }
     }
 
-    public void getStackDockedModeBounds(int stackId, Rect bounds) {
+    public void getStackDockedModeBounds(
+            int stackId, Rect bounds, boolean ignoreVisibilityOnKeyguardShowing) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack != null) {
-                stack.getStackDockedModeBoundsLocked(bounds);
+                stack.getStackDockedModeBoundsLocked(bounds, ignoreVisibilityOnKeyguardShowing);
                 return;
             }
             bounds.setEmpty();
@@ -4822,15 +4855,16 @@
      * @return True if the stack is now fullscreen.
      * */
     public boolean resizeStack(int stackId, Rect bounds,
-            SparseArray<Configuration> configs, SparseArray<Rect> taskBounds) {
+            SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
+            SparseArray<Rect> taskTempInsetBounds) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack == null) {
                 throw new IllegalArgumentException("resizeStack: stackId " + stackId
                         + " not found.");
             }
-            if (stack.setBounds(bounds, configs, taskBounds) && stack.isVisibleLocked()) {
-                stack.resizeWindows();
+            if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
+                    && stack.isVisibleLocked()) {
                 stack.getDisplayContent().layoutNeeded = true;
                 mWindowPlacerLocked.performSurfacePlacement();
             }
@@ -4838,6 +4872,17 @@
         }
     }
 
+    public void prepareFreezingTaskBounds(int stackId) {
+        synchronized (mWindowMap) {
+            final TaskStack stack = mStackIdToStack.get(stackId);
+            if (stack == null) {
+                throw new IllegalArgumentException("prepareFreezingTaskBounds: stackId " + stackId
+                        + " not found.");
+            }
+            stack.prepareFreezingTaskBounds();
+        }
+    }
+
     public void positionTaskInStack(int taskId, int stackId, int position, Rect bounds,
             Configuration config) {
         synchronized (mWindowMap) {
@@ -6263,7 +6308,7 @@
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
         // By updating the Display info here it will be available to
         // computeScreenConfigurationLocked later.
-        updateDisplayAndOrientationLocked();
+        updateDisplayAndOrientationLocked(mCurConfiguration.uiMode);
 
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         if (!inTransaction) {
@@ -6830,16 +6875,17 @@
         return config;
     }
 
-    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int dw, int dh) {
+    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int rotation, int uiMode,
+            int dw, int dh) {
         // TODO: Multidisplay: for now only use with default display.
-        final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation);
+        final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode);
         if (width < displayInfo.smallestNominalAppWidth) {
             displayInfo.smallestNominalAppWidth = width;
         }
         if (width > displayInfo.largestNominalAppWidth) {
             displayInfo.largestNominalAppWidth = width;
         }
-        final int height = mPolicy.getConfigDisplayHeight(dw, dh, rotation);
+        final int height = mPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode);
         if (height < displayInfo.smallestNominalAppHeight) {
             displayInfo.smallestNominalAppHeight = height;
         }
@@ -6849,11 +6895,11 @@
     }
 
     private int reduceConfigLayout(int curLayout, int rotation, float density,
-            int dw, int dh) {
+            int dw, int dh, int uiMode) {
         // TODO: Multidisplay: for now only use with default display.
         // Get the app screen size at this rotation.
-        int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation);
-        int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation);
+        int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode);
+        int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode);
 
         // Compute the screen layout size class for this rotation.
         int longSize = w;
@@ -6869,7 +6915,7 @@
     }
 
     private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, boolean rotated,
-                  int dw, int dh, float density, Configuration outConfig) {
+                  int uiMode, int dw, int dh, float density, Configuration outConfig) {
         // TODO: Multidisplay: for now only use with default display.
 
         // We need to determine the smallest width that will occur under normal
@@ -6888,24 +6934,24 @@
         displayInfo.smallestNominalAppHeight = 1<<30;
         displayInfo.largestNominalAppWidth = 0;
         displayInfo.largestNominalAppHeight = 0;
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, unrotDw, unrotDh);
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, unrotDh, unrotDw);
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, unrotDw, unrotDh);
-        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, unrotDh, unrotDw);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_0, uiMode, unrotDw, unrotDh);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_90, uiMode, unrotDh, unrotDw);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_180, uiMode, unrotDw, unrotDh);
+        adjustDisplaySizeRanges(displayInfo, Surface.ROTATION_270, uiMode, unrotDh, unrotDw);
         int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
         outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
         outConfig.screenLayout = sl;
     }
 
-    private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm,
-            int dw, int dh) {
+    private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
+            DisplayMetrics dm, int dw, int dh) {
         // TODO: Multidisplay: for now only use with default display.
-        dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation);
-        dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation);
+        dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode);
+        dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode);
         float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
         int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
         if (curSize == 0 || size < curSize) {
@@ -6914,7 +6960,7 @@
         return curSize;
     }
 
-    private int computeCompatSmallestWidth(boolean rotated, DisplayMetrics dm, int dw, int dh) {
+    private int computeCompatSmallestWidth(boolean rotated, int uiMode, DisplayMetrics dm, int dw, int dh) {
         // TODO: Multidisplay: for now only use with default display.
         mTmpDisplayMetrics.setTo(dm);
         final DisplayMetrics tmpDm = mTmpDisplayMetrics;
@@ -6926,15 +6972,15 @@
             unrotDw = dw;
             unrotDh = dh;
         }
-        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, tmpDm, unrotDw, unrotDh);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, tmpDm, unrotDh, unrotDw);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, tmpDm, unrotDw, unrotDh);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, tmpDm, unrotDh, unrotDw);
+        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw);
         return sw;
     }
 
     /** Do not call if mDisplayReady == false */
-    DisplayInfo updateDisplayAndOrientationLocked() {
+    DisplayInfo updateDisplayAndOrientationLocked(int uiMode) {
         // TODO(multidisplay): For now, apply Configuration to main screen only.
         final DisplayContent displayContent = getDefaultDisplayContentLocked();
 
@@ -6965,8 +7011,8 @@
         }
 
         // Update application display metrics.
-        final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
-        final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
+        final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode);
+        final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode);
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         displayInfo.rotation = mRotation;
         displayInfo.logicalWidth = dw;
@@ -6998,20 +7044,24 @@
 
     /** Do not call if mDisplayReady == false */
     void computeScreenConfigurationLocked(Configuration config) {
-        final DisplayInfo displayInfo = updateDisplayAndOrientationLocked();
+        final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(
+                config.uiMode);
 
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
         config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
                 Configuration.ORIENTATION_LANDSCAPE;
         config.screenWidthDp =
-                (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation) / mDisplayMetrics.density);
+                (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation, config.uiMode) /
+                        mDisplayMetrics.density);
         config.screenHeightDp =
-                (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation) / mDisplayMetrics.density);
+                (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation, config.uiMode) /
+                        mDisplayMetrics.density);
         final boolean rotated = (mRotation == Surface.ROTATION_90
                 || mRotation == Surface.ROTATION_270);
-        computeSizeRangesAndScreenLayout(displayInfo, rotated, dw, dh, mDisplayMetrics.density,
-                config);
+
+        computeSizeRangesAndScreenLayout(displayInfo, rotated, config.uiMode, dw, dh,
+                mDisplayMetrics.density, config);
 
         config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
                 | ((displayInfo.flags & Display.FLAG_ROUND) != 0
@@ -7020,7 +7070,7 @@
 
         config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
         config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
-        config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated,
+        config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode,
                 mDisplayMetrics, dw, dh);
         config.densityDpi = displayInfo.logicalDensityDpi;
 
@@ -7079,9 +7129,6 @@
             mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
             mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
         }
-        if (mShowImeWithHardKeyboard) {
-            config.keyboard = Configuration.KEYBOARD_NOKEYS;
-        }
 
         // Let the policy update hidden states.
         config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
@@ -7090,18 +7137,6 @@
         mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
     }
 
-    public void updateShowImeWithHardKeyboard() {
-        synchronized (mWindowMap) {
-            final boolean showImeWithHardKeyboard = Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0,
-                    mCurrentUserId) == 1;
-            if (mShowImeWithHardKeyboard != showImeWithHardKeyboard) {
-                mShowImeWithHardKeyboard = showImeWithHardKeyboard;
-                mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
-            }
-        }
-    }
-
     void notifyHardKeyboardStatusChange() {
         final boolean available;
         final WindowManagerInternal.OnHardKeyboardStatusChangeListener listener;
@@ -7150,18 +7185,25 @@
         } catch(RemoteException e) {}
     }
 
-    private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
-        Task task = null;
+    private void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
+        int taskId = -1;
         synchronized (mWindowMap) {
-            task = displayContent.findTaskForControlPoint(startX, startY);
-            if (task == null || !startPositioningLocked(
-                    task.getTopVisibleAppMainWindow(), true /*resize*/, startX, startY)) {
-                return;
+            final Task task = displayContent.findTaskForControlPoint(x, y);
+            if (task != null) {
+                if (!startPositioningLocked(
+                        task.getTopVisibleAppMainWindow(), true /*resize*/, x, y)) {
+                    return;
+                }
+                taskId = task.mTaskId;
+            } else {
+                taskId = displayContent.taskIdFromPoint(x, y);
             }
         }
-        try {
-            mActivityManager.setFocusedTask(task.mTaskId);
-        } catch(RemoteException e) {}
+        if (taskId >= 0) {
+            try {
+                mActivityManager.setFocusedTask(taskId);
+            } catch(RemoteException e) {}
+        }
     }
 
     private boolean startPositioningLocked(
@@ -7476,18 +7518,17 @@
         public static final int RESET_ANR_MESSAGE = 38;
         public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
 
-        public static final int TAP_DOWN_OUTSIDE_TASK = 40;
-        public static final int FINISH_TASK_POSITIONING = 41;
+        public static final int FINISH_TASK_POSITIONING = 40;
 
-        public static final int UPDATE_DOCKED_STACK_DIVIDER = 42;
+        public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
 
-        public static final int RESIZE_STACK = 43;
-        public static final int RESIZE_TASK = 44;
+        public static final int RESIZE_STACK = 42;
+        public static final int RESIZE_TASK = 43;
 
-        public static final int TWO_FINGER_SCROLL_START = 45;
-        public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 46;
+        public static final int TWO_FINGER_SCROLL_START = 44;
+        public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 45;
 
-        public static final int WINDOW_REPLACEMENT_TIMEOUT = 47;
+        public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -7941,27 +7982,13 @@
                     }
                     break;
 
-                case TAP_OUTSIDE_TASK: {
-                    int taskId;
-                    synchronized (mWindowMap) {
-                        taskId = ((DisplayContent)msg.obj).taskIdFromPoint(msg.arg1, msg.arg2);
-                    }
-                    if (taskId >= 0) {
-                        try {
-                            mActivityManager.setFocusedTask(taskId);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                }
-                break;
-
                 case TWO_FINGER_SCROLL_START: {
                     startScrollingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
                 }
                 break;
 
-                case TAP_DOWN_OUTSIDE_TASK: {
-                    startResizingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
+                case TAP_OUTSIDE_TASK: {
+                    handleTapOutsideTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
                 }
                 break;
 
@@ -8074,7 +8101,7 @@
                 case WINDOW_REPLACEMENT_TIMEOUT: {
                     final AppWindowToken token = (AppWindowToken) msg.obj;
                     synchronized (mWindowMap) {
-                        token.clearTimedoutReplaceesLocked();
+                        token.clearTimedoutReplacesLocked();
                     }
                 }
                 break;
@@ -8439,6 +8466,7 @@
         boolean configChanged = updateOrientationFromAppTokensLocked(false);
         mTempConfiguration.setToDefaults();
         mTempConfiguration.fontScale = mCurConfiguration.fontScale;
+        mTempConfiguration.uiMode = mCurConfiguration.uiMode;
         computeScreenConfigurationLocked(mTempConfiguration);
         configChanged |= mCurConfiguration.diff(mTempConfiguration) != 0;
 
@@ -9078,8 +9106,8 @@
                         if (wtoken == token) {
                             break;
                         }
-                        if (mFocusedApp == token && token.stackCanReceiveKeys()) {
-                            // Whoops, we are below the focused app whose stack can receive keys...
+                        if (mFocusedApp == token && token.windowsAreFocusable()) {
+                            // Whoops, we are below the focused app whose windows are focusable...
                             // No focus for you!!!
                             if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM,
                                     "findFocusedWindow: Reached focused app=" + mFocusedApp);
@@ -9464,6 +9492,32 @@
         }
     }
 
+    public void notifyAppRelaunching(IBinder token) {
+        synchronized (mWindowMap) {
+            AppWindowToken appWindow = findAppWindowToken(token);
+            if (canFreezeBounds(appWindow)) {
+                appWindow.freezeBounds();
+            }
+        }
+    }
+
+    public void notifyAppRelaunchingFinished(IBinder token) {
+        synchronized (mWindowMap) {
+            AppWindowToken appWindow = findAppWindowToken(token);
+            if (canFreezeBounds(appWindow)) {
+                appWindow.unfreezeBounds();
+            }
+        }
+    }
+
+    private boolean canFreezeBounds(AppWindowToken appWindow) {
+
+        // For freeform windows, we can't freeze the bounds at the moment because this would make
+        // the resizing unresponsive.
+        return appWindow != null && appWindow.mTask != null
+                && !appWindow.mTask.inFreeformWorkspace();
+    }
+
     void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
         pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
         mPolicy.dump("    ", pw, args);
@@ -9729,17 +9783,22 @@
     boolean dumpWindows(PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll) {
         WindowList windows = new WindowList();
-        if ("visible".equals(name) || "visible-apps".equals(name)) {
-            final boolean appsOnly = "visible-apps".equals(name);
+        if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) {
+            final boolean appsOnly = name.contains("apps");
+            final boolean visibleOnly = name.contains("visible");
             synchronized(mWindowMap) {
+                if (appsOnly) {
+                    dumpDisplayContentsLocked(pw, true);
+                }
+
                 final int numDisplays = mDisplayContents.size();
                 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                     final WindowList windowList =
                             mDisplayContents.valueAt(displayNdx).getWindowList();
                     for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
                         final WindowState w = windowList.get(winNdx);
-                        if (w.mWinAnimator.getShown()
-                                && (!appsOnly || (appsOnly && w.mAppToken != null))) {
+                        if ((!visibleOnly || w.mWinAnimator.getShown())
+                                && (!appsOnly || w.mAppToken != null)) {
                             windows.add(w);
                         }
                     }
@@ -10128,16 +10187,40 @@
         synchronized (mWindowMap) {
             appWindowToken = findAppWindowToken(token);
             if (appWindowToken == null || !appWindowToken.isVisible()) {
-                Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
+                Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+                        + token);
                 return;
             }
             appWindowToken.setReplacingWindows(animate);
         }
+    }
 
-        if (appWindowToken != null) {
-            mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
-            mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
-                    WINDOW_REPLACEMENT_TIMEOUT_DURATION);
+    /**
+     * If we're replacing the window, schedule a timer to clear the replaced window
+     * after a timeout, in case the replacing window is not coming.
+     *
+     * If we're not replacing the window, clear the replace window settings of the app.
+     *
+     * @param token Application token for the activity whose window might be replaced.
+     * @param replacing Whether the window is being replaced or not.
+     */
+    public void scheduleClearReplacingWindowIfNeeded(IBinder token, boolean replacing) {
+        AppWindowToken appWindowToken = null;
+        synchronized (mWindowMap) {
+            appWindowToken = findAppWindowToken(token);
+            if (appWindowToken == null) {
+                Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
+                        + token);
+                return;
+            }
+            if (replacing) {
+                mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+                mH.sendMessageDelayed(
+                        mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
+                        WINDOW_REPLACEMENT_TIMEOUT_DURATION);
+            } else {
+                appWindowToken.resetReplacingWindows();
+            }
         }
     }
 
@@ -10157,6 +10240,14 @@
         }
     }
 
+    @Override
+    public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
+        synchronized (mWindowMap) {
+            getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
+                    visible, targetStackId, alpha);
+        }
+    }
+
     public void setTaskResizeable(int taskId, boolean resizeable) {
         synchronized (mWindowMap) {
             Task task = mTaskIdToTask.get(taskId);
@@ -10175,25 +10266,21 @@
     }
 
     @Override
-    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+    public void registerDockedStackListener(IDockedStackListener listener) {
         if (!checkCallingPermission(android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS,
-                "registerDockDividerVisibilityListener()")) {
+                "registerDockedStackListener()")) {
             return;
         }
         // TODO(multi-display): The listener is registered on the default display only.
-        final DockedStackDividerController controller =
-                getDefaultDisplayContentLocked().getDockedDividerController();
-        controller.registerDockDividerVisibilityListener(listener);
+        getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener(
+                listener);
+    }
+
+    @Override
+    public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
         try {
-            listener.asBinder().linkToDeath(new IBinder.DeathRecipient() {
-                @Override
-                public void binderDied() {
-                    getDefaultDisplayContentLocked().getDockedDividerController()
-                            .registerDockDividerVisibilityListener(null);
-                }
-            }, 0);
+            getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver);
         } catch (RemoteException e) {
-            controller.registerDockDividerVisibilityListener(null);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 733bc29..e8a02b0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -309,6 +309,12 @@
     // possible to draw.
     final Rect mOutsetFrame = new Rect();
 
+    /**
+     * Usually empty. Set to the task's tempInsetFrame. See
+     *{@link android.app.IActivityManager#resizeDockedStack}.
+     */
+    final Rect mInsetFrame = new Rect();
+
     boolean mContentChanged;
 
     // If a window showing a wallpaper: the requested offset for the
@@ -614,8 +620,18 @@
             // We use the parent frame as the containing frame for fullscreen and child windows
             mContainingFrame.set(pf);
             mDisplayFrame.set(df);
+            mInsetFrame.setEmpty();
         } else {
             task.getBounds(mContainingFrame);
+            task.getTempInsetBounds(mInsetFrame);
+            if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
+
+                // If the bounds are frozen, we still want to translate the window freely and only
+                // freeze the size.
+                Rect frozen = mAppToken.mFrozenBounds.peek();
+                mContainingFrame.right = mContainingFrame.left + frozen.width();
+                mContainingFrame.bottom = mContainingFrame.top + frozen.height();
+            }
             final WindowState imeWin = mService.mInputMethodWindow;
             if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this
                     && mContainingFrame.bottom > cf.bottom) {
@@ -674,6 +690,12 @@
             mOutsets.set(0, 0, 0, 0);
         }
 
+        // Denotes the actual frame used to calculate the insets. When resizing in docked mode,
+        // we'd like to freeze the layout, so we also need to freeze the insets temporarily. By the
+        // notion of a task having a different inset frame, we can achieve that while still moving
+        // the task around.
+        final Rect frame = !mInsetFrame.isEmpty() ? mInsetFrame : mFrame;
+
         // Make sure the content and visible frames are inside of the
         // final window frame.
         if (freeformWorkspace && !mFrame.isEmpty()) {
@@ -707,44 +729,81 @@
                 // will return the correct value to the renderer.
                 mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
                 mContentFrame.set(mFrame);
+                if (!mFrame.equals(mLastFrame)) {
+                    mMovedByResize = true;
+                }
             }
         } else {
-            mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
-                    Math.max(mContentFrame.top, mFrame.top),
-                    Math.min(mContentFrame.right, mFrame.right),
-                    Math.min(mContentFrame.bottom, mFrame.bottom));
+            mContentFrame.set(Math.max(mContentFrame.left, frame.left),
+                    Math.max(mContentFrame.top, frame.top),
+                    Math.min(mContentFrame.right, frame.right),
+                    Math.min(mContentFrame.bottom, frame.bottom));
 
-            mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
-                    Math.max(mVisibleFrame.top, mFrame.top),
-                    Math.min(mVisibleFrame.right, mFrame.right),
-                    Math.min(mVisibleFrame.bottom, mFrame.bottom));
+            mVisibleFrame.set(Math.max(mVisibleFrame.left, frame.left),
+                    Math.max(mVisibleFrame.top, frame.top),
+                    Math.min(mVisibleFrame.right, frame.right),
+                    Math.min(mVisibleFrame.bottom, frame.bottom));
 
-            mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
-                    Math.max(mStableFrame.top, mFrame.top),
-                    Math.min(mStableFrame.right, mFrame.right),
-                    Math.min(mStableFrame.bottom, mFrame.bottom));
+            mStableFrame.set(Math.max(mStableFrame.left, frame.left),
+                    Math.max(mStableFrame.top, frame.top),
+                    Math.min(mStableFrame.right, frame.right),
+                    Math.min(mStableFrame.bottom, frame.bottom));
         }
 
-        mOverscanInsets.set(Math.max(mOverscanFrame.left - mFrame.left, 0),
-                Math.max(mOverscanFrame.top - mFrame.top, 0),
-                Math.max(mFrame.right - mOverscanFrame.right, 0),
-                Math.max(mFrame.bottom - mOverscanFrame.bottom, 0));
+        mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
+                Math.max(mOverscanFrame.top - frame.top, 0),
+                Math.max(frame.right - mOverscanFrame.right, 0),
+                Math.max(frame.bottom - mOverscanFrame.bottom, 0));
 
-        mContentInsets.set(mContentFrame.left - mFrame.left,
-                mContentFrame.top - mFrame.top,
-                mFrame.right - mContentFrame.right,
-                mFrame.bottom - mContentFrame.bottom);
 
-        mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
-                mVisibleFrame.top - mFrame.top,
-                mFrame.right - mVisibleFrame.right,
-                mFrame.bottom - mVisibleFrame.bottom);
 
-        mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
-                Math.max(mStableFrame.top - mFrame.top, 0),
-                Math.max(mFrame.right - mStableFrame.right, 0),
-                Math.max(mFrame.bottom - mStableFrame.bottom, 0));
+        if (mAttrs.type == TYPE_DOCK_DIVIDER) {
 
+            // For the docked divider, we calculate the stable insets like a full-screen window
+            // so it can use it to calculate the snap positions.
+            mStableInsets.set(Math.max(mStableFrame.left - mDisplayFrame.left, 0),
+                    Math.max(mStableFrame.top - mDisplayFrame.top, 0),
+                    Math.max(mDisplayFrame.right - mStableFrame.right, 0),
+                    Math.max(mDisplayFrame.bottom - mStableFrame.bottom, 0));
+
+            // The divider doesn't care about insets in any case, so set it to empty so we don't
+            // trigger a relayout when moving it.
+            mContentInsets.setEmpty();
+            mVisibleInsets.setEmpty();
+        } else {
+            mContentInsets.set(mContentFrame.left - frame.left,
+                    mContentFrame.top - frame.top,
+                    frame.right - mContentFrame.right,
+                    frame.bottom - mContentFrame.bottom);
+
+            mVisibleInsets.set(mVisibleFrame.left - frame.left,
+                    mVisibleFrame.top - frame.top,
+                    frame.right - mVisibleFrame.right,
+                    frame.bottom - mVisibleFrame.bottom);
+
+            mStableInsets.set(Math.max(mStableFrame.left - frame.left, 0),
+                    Math.max(mStableFrame.top - frame.top, 0),
+                    Math.max(frame.right - mStableFrame.right, 0),
+                    Math.max(frame.bottom - mStableFrame.bottom, 0));
+        }
+
+        if (!mInsetFrame.isEmpty()) {
+            mContentFrame.set(mFrame);
+            mContentFrame.top += mContentInsets.top;
+            mContentFrame.bottom += mContentInsets.bottom;
+            mContentFrame.left += mContentInsets.left;
+            mContentFrame.right += mContentInsets.right;
+            mVisibleFrame.set(mFrame);
+            mVisibleFrame.top += mVisibleInsets.top;
+            mVisibleFrame.bottom += mVisibleInsets.bottom;
+            mVisibleFrame.left += mVisibleInsets.left;
+            mVisibleFrame.right += mVisibleInsets.right;
+            mStableFrame.set(mFrame);
+            mStableFrame.top += mStableInsets.top;
+            mStableFrame.bottom += mStableInsets.bottom;
+            mStableFrame.left += mStableInsets.left;
+            mStableFrame.right += mStableInsets.right;
+        }
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -996,61 +1055,59 @@
     }
 
     /**
-     * Is this window visible?  It is not visible if there is no
-     * surface, or we are in the process of running an exit animation
-     * that will remove the surface, or its app token has been hidden.
+     * Does the minimal check for visibility. Callers generally want to use one of the public
+     * methods as they perform additional checks on the app token.
+     * TODO: See if there are other places we can use this check below instead of duplicating...
      */
-    @Override
-    public boolean isVisibleLw() {
-        final AppWindowToken atoken = mAppToken;
+    private boolean isVisibleUnchecked() {
         return mHasSurface && mPolicyVisibility && !mAttachedHidden
-                && (atoken == null || !atoken.hiddenRequested)
-                && !mExiting && !mDestroying;
+                && !mExiting && !mDestroying && (!mIsWallpaper || mWallpaperVisible);
     }
 
     /**
-     * Like {@link #isVisibleLw}, but also counts a window that is currently
-     * "hidden" behind the keyguard as visible.  This allows us to apply
-     * things like window flags that impact the keyguard.
-     * XXX I am starting to think we need to have ANOTHER visibility flag
-     * for this "hidden behind keyguard" state rather than overloading
-     * mPolicyVisibility.  Ungh.
+     * Is this window visible?  It is not visible if there is no surface, or we are in the process
+     * of running an exit animation that will remove the surface, or its app token has been hidden.
+     */
+    @Override
+    public boolean isVisibleLw() {
+        return (mAppToken == null || !mAppToken.hiddenRequested) && isVisibleUnchecked();
+    }
+
+    /**
+     * Like {@link #isVisibleLw}, but also counts a window that is currently "hidden" behind the
+     * keyguard as visible.  This allows us to apply things like window flags that impact the
+     * keyguard. XXX I am starting to think we need to have ANOTHER visibility flag for this
+     * "hidden behind keyguard" state rather than overloading mPolicyVisibility.  Ungh.
      */
     @Override
     public boolean isVisibleOrBehindKeyguardLw() {
-        if (mRootToken.waitingToShow &&
-                mService.mAppTransition.isTransitionSet()) {
+        if (mRootToken.waitingToShow && mService.mAppTransition.isTransitionSet()) {
             return false;
         }
         final AppWindowToken atoken = mAppToken;
         final boolean animating = atoken != null && atoken.mAppAnimator.animation != null;
         return mHasSurface && !mDestroying && !mExiting
                 && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
-                && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
-                                && !mRootToken.hidden)
+                && ((!mAttachedHidden && mViewVisibility == View.VISIBLE && !mRootToken.hidden)
                         || mWinAnimator.mAnimation != null || animating);
     }
 
     /**
-     * Is this window visible, ignoring its app token?  It is not visible
-     * if there is no surface, or we are in the process of running an exit animation
-     * that will remove the surface.
+     * Is this window visible, ignoring its app token? It is not visible if there is no surface,
+     * or we are in the process of running an exit animation that will remove the surface.
      */
     public boolean isWinVisibleLw() {
-        final AppWindowToken atoken = mAppToken;
-        return mHasSurface && mPolicyVisibility && !mAttachedHidden
-                && (atoken == null || !atoken.hiddenRequested || atoken.mAppAnimator.animating)
-                && !mExiting && !mDestroying;
+        return (mAppToken == null || !mAppToken.hiddenRequested || mAppToken.mAppAnimator.animating)
+                && isVisibleUnchecked();
     }
 
     /**
-     * The same as isVisible(), but follows the current hidden state of
-     * the associated app token, not the pending requested hidden state.
+     * The same as isVisible(), but follows the current hidden state of the associated app token,
+     * not the pending requested hidden state.
      */
     boolean isVisibleNow() {
-        return mHasSurface && mPolicyVisibility && !mAttachedHidden
-                && (!mRootToken.hidden || mAttrs.type == TYPE_APPLICATION_STARTING)
-                && !mExiting && !mDestroying;
+        return (!mRootToken.hidden || mAttrs.type == TYPE_APPLICATION_STARTING)
+                && isVisibleUnchecked();
     }
 
     /**
@@ -1109,8 +1166,7 @@
             return false;
         }
         return mHasSurface && mPolicyVisibility && !mDestroying
-                && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
-                                && !mRootToken.hidden)
+                && ((!mAttachedHidden && mViewVisibility == View.VISIBLE && !mRootToken.hidden)
                         || mWinAnimator.mAnimation != null
                         || ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
     }
@@ -1130,8 +1186,7 @@
             return false;
         }
         return mHasSurface && !mDestroying
-                && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
-                                && !mRootToken.hidden)
+                && ((!mAttachedHidden && mViewVisibility == View.VISIBLE && !mRootToken.hidden)
                         || mWinAnimator.mAnimation != null
                         || ((atoken != null) && (atoken.mAppAnimator.animation != null)
                                 && !mWinAnimator.isDummyAnimation()));
@@ -1409,15 +1464,22 @@
     }
 
     boolean inDockedWorkspace() {
-        Task task = getTask();
+        final Task task = getTask();
         return task != null && task.inDockedWorkspace();
     }
 
     boolean isDockedInEffect() {
-        Task task = getTask();
+        final Task task = getTask();
         return task != null && task.isDockedInEffect();
     }
 
+    void applyScrollIfNeeded() {
+        final Task task = getTask();
+        if (task != null) {
+            task.applyScrollToWindowIfNeeded(this);
+        }
+    }
+
     int getTouchableRegion(Region region, int flags) {
         final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
         if (modal && mAppToken != null) {
@@ -1558,12 +1620,7 @@
         return isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE)
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
-                && stackCanReceiveKeys();
-    }
-
-    boolean stackCanReceiveKeys() {
-        final TaskStack stack = getStack();
-        return stack != null && StackId.canReceiveKeys(stack.mStackId);
+                && (mAppToken == null || mAppToken.windowsAreFocusable());
     }
 
     @Override
@@ -1942,21 +1999,24 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
-    private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
-            Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            Configuration newConfig) throws RemoteException {
-        DisplayInfo displayInfo = getDisplayInfo();
-        mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+    Rect getBackdropFrame(Rect frame) {
         // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
         // start even if we haven't received the relayout window, so that the client requests
         // the relayout sooner. When dragging stops, backDropFrame needs to stay fullscreen
         // until the window to small size, otherwise the multithread renderer will shift last
         // one or more frame to wrong offset. So here we send fullscreen backdrop if either
         // isDragResizing() or isDragResizeChanged() is true.
+        DisplayInfo displayInfo = getDisplayInfo();
+        mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
         boolean resizing = isDragResizing() || isDragResizeChanged();
-        final Rect backDropFrame = (inFreeformWorkspace() || !resizing) ? frame : mTmpRect;
-        mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
-                outsets, reportDraw, newConfig, backDropFrame);
+        return (inFreeformWorkspace() || !resizing) ? frame : mTmpRect;
+    }
+
+    private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
+            Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
+            Configuration newConfig) throws RemoteException {
+        mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
+                reportDraw, newConfig, getBackdropFrame(frame));
     }
 
     public void registerFocusObserver(IWindowFocusObserver observer) {
@@ -2003,7 +2063,13 @@
         if (task.isDragResizing()) {
             return true;
         }
-        return mDisplayContent.mDividerControllerLocked.isResizing() &&
+
+        // If the bounds are currently frozen, it means that the layout size that the app sees
+        // and the bounds we clip this window to might be different. In order to avoid holes, we
+        // simulate that we are still resizing so the app fills the hole with the resizing
+        // background.
+        return (mDisplayContent.mDividerControllerLocked.isResizing()
+                        || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
                 !task.inFreeformWorkspace() && !task.isFullscreen();
     }
 
@@ -2225,10 +2291,12 @@
 
     void transformFromScreenToSurfaceSpace(Rect rect) {
          if (mHScale >= 0) {
-            rect.right = rect.left + (int)((rect.right - rect.left) / mHScale);
+            rect.left = (int) (rect.left / mHScale);
+            rect.right = (int) (rect.right / mHScale);
         }
         if (mVScale >= 0) {
-            rect.bottom = rect.top + (int)((rect.bottom - rect.top) / mVScale);
+            rect.top = (int) (rect.top / mVScale);
+            rect.bottom = (int) (rect.bottom / mVScale);
         }
     }
 
@@ -2302,10 +2370,21 @@
     }
 
     void setReplacing(boolean animate) {
-        if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) == 0) {
-            mWillReplaceWindow = true;
-            mReplacingWindow = null;
-            mAnimateReplacingWindow = animate;
+        if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0
+                || mAttrs.type == TYPE_APPLICATION_STARTING) {
+            // We don't set replacing on starting windows since they are added by window manager and
+            // not the client so won't be replaced by the client.
+            return;
         }
+
+        mWillReplaceWindow = true;
+        mReplacingWindow = null;
+        mAnimateReplacingWindow = animate;
+    }
+
+    void resetReplacing() {
+        mWillReplaceWindow = false;
+        mReplacingWindow = null;
+        mAnimateReplacingWindow = false;
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 381db56..a3a59c0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -37,6 +37,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -1063,7 +1064,16 @@
         final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
-        mSystemDecorRect.set(0, 0, width, height);
+        if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) {
+
+            // If we are resizing with the divider, the task bounds might be smaller than the
+            // stack bounds. The system decor is used to clip to the task bounds, which we don't
+            // want in this case in order to avoid holes.
+            final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
+            mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+        } else {
+            mSystemDecorRect.set(0, 0, width, height);
+        }
 
         // If a freeform window is animating from a position where it would be cutoff, it would be
         // cutoff during the animation. We don't want that, so for the duration of the animation
@@ -1345,6 +1355,11 @@
                 }
             }
             w.mToken.hasVisible = true;
+
+            final Task task = w.getTask();
+            if (task != null) {
+                task.scheduleShowNonResizeableDockToastIfNeeded();
+            }
         }
     }
 
@@ -1503,10 +1518,6 @@
                 mWin.mAppToken.updateReportedVisibilityLocked();
             }
 
-            final Task task = mWin.getTask();
-            if (task != null) {
-                task.scheduleShowNonResizeableDockToastIfNeeded();
-            }
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6ebda07..f77e5a6 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -739,13 +739,15 @@
                             }
                         }
                     }
-                    /*
-                     * Updates the shown frame before we set up the surface. This is needed because
-                     * the resizing could change the top-left position (in addition to size) of the
-                     * window. setSurfaceBoundariesLocked uses mShownPosition to position the
-                      * surface.
-                     */
-                    winAnimator.computeShownFrameLocked();
+                    if (!winAnimator.isAnimating()) {
+                        // Updates the shown frame before we set up the surface. This is needed
+                        // because the resizing could change the top-left position (in addition to
+                        // size) of the window. setSurfaceBoundariesLocked uses mShownPosition to
+                        // position the surface. We only apply it to windows that aren't animating,
+                        // because we depend on the animation to calculate the correct shown frame
+                        // on the next animation step.
+                        winAnimator.computeShownFrameLocked();
+                    }
                     winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
                 }
 
@@ -857,7 +859,8 @@
                     + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
         }
 
-        mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation);
+        mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
+                mService.mCurConfiguration.uiMode);
         if (isDefaultDisplay) {
             // Not needed on non-default displays.
             mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cf661ce..f14b032 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -129,6 +129,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.pm.UserManagerService;
 import com.android.server.pm.UserRestrictionsUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -253,6 +254,7 @@
     final IPackageManager mIPackageManager;
     final UserManager mUserManager;
     final UserManagerInternal mUserManagerInternal;
+    private final LockPatternUtils mLockPatternUtils;
 
     final LocalService mLocalService;
 
@@ -387,6 +389,7 @@
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
         private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+        private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
         private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING
                 = "disable-bt-contacts-sharing";
         private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
@@ -424,6 +427,9 @@
         private static final String TAG_PACKAGE_LIST_ITEM  = "item";
         private static final String TAG_KEEP_UNINSTALLED_PACKAGES  = "keep-uninstalled-packages";
         private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
+        private static final String TAG_SHORT_SUPPORT_MESSAGE = "short-support-message";
+        private static final String TAG_LONG_SUPPORT_MESSAGE = "long-support-message";
+        private static final String TAG_PARENT_ADMIN = "parent-admin";
 
         final DeviceAdminInfo info;
 
@@ -472,10 +478,14 @@
         boolean encryptionRequested = false;
         boolean disableCamera = false;
         boolean disableCallerId = false;
+        boolean disableContactsSearch = false;
         boolean disableBluetoothContactSharing = true;
         boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
         boolean requireAutoTime = false; // Can only be set by a device owner.
 
+        ActiveAdmin parentAdmin;
+        final boolean isParent;
+
         static class TrustAgentInfo {
             public PersistableBundle options;
             TrustAgentInfo(PersistableBundle bundle) {
@@ -509,8 +519,20 @@
 
         Bundle userRestrictions;
 
-        ActiveAdmin(DeviceAdminInfo _info) {
+        // Support text provided by the admin to display to the user.
+        String shortSupportMessage = null;
+        String longSupportMessage = null;
+
+        ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
             info = _info;
+            isParent = parent;
+        }
+
+        ActiveAdmin getParentActiveAdmin() {
+            if (parentAdmin == null && !isParent) {
+                parentAdmin = new ActiveAdmin(info, /* parent */ true);
+            }
+            return parentAdmin;
         }
 
         int getUid() { return info.getActivityInfo().applicationInfo.uid; }
@@ -619,6 +641,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
                 out.endTag(null, TAG_DISABLE_CALLER_ID);
             }
+            if (disableContactsSearch) {
+                out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch));
+                out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+            }
             if (disableBluetoothContactSharing) {
                 out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
                 out.attribute(null, ATTR_VALUE,
@@ -688,6 +715,21 @@
                 UserRestrictionsUtils.writeRestrictions(
                         out, userRestrictions, TAG_USER_RESTRICTIONS);
             }
+            if (!TextUtils.isEmpty(shortSupportMessage)) {
+                out.startTag(null, TAG_SHORT_SUPPORT_MESSAGE);
+                out.text(shortSupportMessage);
+                out.endTag(null, TAG_SHORT_SUPPORT_MESSAGE);
+            }
+            if (!TextUtils.isEmpty(longSupportMessage)) {
+                out.startTag(null, TAG_LONG_SUPPORT_MESSAGE);
+                out.text(longSupportMessage);
+                out.endTag(null, TAG_LONG_SUPPORT_MESSAGE);
+            }
+            if (parentAdmin != null) {
+                out.startTag(null, TAG_PARENT_ADMIN);
+                parentAdmin.writeToXml(out);
+                out.endTag(null, TAG_PARENT_ADMIN);
+            }
         }
 
         void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -775,6 +817,9 @@
                 } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
                     disableCallerId = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) {
+                    disableContactsSearch = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) {
                     disableBluetoothContactSharing = Boolean.parseBoolean(parser
                             .getAttributeValue(null, ATTR_VALUE));
@@ -801,6 +846,23 @@
                     keepUninstalledPackages = readPackageList(parser, tag);
                 } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
                     UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
+                } else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) {
+                    type = parser.next();
+                    if (type == XmlPullParser.TEXT) {
+                        shortSupportMessage = parser.getText();
+                    } else {
+                        Log.w(LOG_TAG, "Missing text when loading short support message");
+                    }
+                } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
+                    type = parser.next();
+                    if (type == XmlPullParser.TEXT) {
+                        longSupportMessage = parser.getText();
+                    } else {
+                        Log.w(LOG_TAG, "Missing text when loading long support message");
+                    }
+                } else if (TAG_PARENT_ADMIN.equals(tag)) {
+                    parentAdmin = new ActiveAdmin(info, /* parent */ true);
+                    parentAdmin.readFromXml(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -983,6 +1045,8 @@
                     pw.println(disableCamera);
             pw.print(prefix); pw.print("disableCallerId=");
                     pw.println(disableCallerId);
+            pw.print(prefix); pw.print("disableContactsSearch=");
+                    pw.println(disableContactsSearch);
             pw.print(prefix); pw.print("disableBluetoothContactSharing=");
                     pw.println(disableBluetoothContactSharing);
             pw.print(prefix); pw.print("disableScreenCapture=");
@@ -1266,6 +1330,7 @@
         mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
 
         mLocalService = new LocalService();
+        mLockPatternUtils = new LockPatternUtils(mContext);
 
         mHasFeature = mContext.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
@@ -1599,6 +1664,23 @@
         }
     }
 
+    /**
+     * Find the admin for the component and userId bit of the uid, then check
+     * the admin's uid matches the uid.
+     */
+    private ActiveAdmin getActiveAdminForUidLocked(ComponentName who, int uid) {
+        final int userId = UserHandle.getUserId(uid);
+        final DevicePolicyData policy = getUserData(userId);
+        ActiveAdmin admin = policy.mAdminMap.get(who);
+        if (admin == null) {
+            throw new SecurityException("No active admin " + who);
+        }
+        if (admin.getUid() != uid) {
+            throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
+        }
+        return admin;
+    }
+
     private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy,
             int uid) {
         // Try to find an admin which can use reqPolicy
@@ -1610,8 +1692,7 @@
                 throw new SecurityException("No active admin " + who);
             }
             if (admin.getUid() != uid) {
-                throw new SecurityException("Admin " + who + " is not owned by uid "
-                        + mInjector.binderGetCallingUid());
+                throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
             }
             if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) {
                 return admin;
@@ -1741,10 +1822,10 @@
         if (!mHasFeature) {
             return null;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         Intent resolveIntent = new Intent();
         resolveIntent.setComponent(adminName);
-        List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
+        List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceiversAsUser(
                 resolveIntent,
                 PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
                 userHandle);
@@ -1968,7 +2049,7 @@
                                     + userHandle);
                         }
                         if (dai != null) {
-                            ActiveAdmin ap = new ActiveAdmin(dai);
+                            ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
                             ap.readFromXml(parser);
                             policy.mAdminMap.put(ap.info.getComponent(), ap);
                         }
@@ -2344,7 +2425,7 @@
             Bundle onEnableData) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
 
         DevicePolicyData policy = getUserData(userHandle);
         DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
@@ -2359,7 +2440,7 @@
                         && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
                     throw new IllegalArgumentException("Admin is already added");
                 }
-                ActiveAdmin newAdmin = new ActiveAdmin(info);
+                ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
                 policy.mAdminMap.put(adminReceiver, newAdmin);
                 int replaceIndex = -1;
                 final int N = policy.mAdminList.size();
@@ -2390,7 +2471,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
         }
@@ -2401,7 +2482,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             DevicePolicyData policyData = getUserData(userHandle);
             return policyData.mRemovingAdmins.contains(adminReceiver);
@@ -2413,7 +2494,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
             if (administrator == null) {
@@ -2430,7 +2511,7 @@
             return Collections.EMPTY_LIST;
         }
 
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
@@ -2450,7 +2531,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
@@ -2468,7 +2549,7 @@
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
             if (admin == null) {
@@ -2494,8 +2575,20 @@
         }
     }
 
+    private boolean isAdminApiLevelMOrBelow(@NonNull ComponentName who, int userHandle) {
+        DeviceAdminInfo adminInfo = findAdmin(who, userHandle, false);
+        return adminInfo.getActivityInfo().applicationInfo.targetSdkVersion
+                <= Build.VERSION_CODES.M;
+    }
+
     @Override
-    public void setPasswordQuality(ComponentName who, int quality) {
+    public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+        ComponentName profileOwner = getProfileOwner(userHandle);
+        return !isAdminApiLevelMOrBelow(profileOwner, userHandle);
+    }
+
+    @Override
+    public void setPasswordQuality(ComponentName who, int quality, boolean parent) {
         if (!mHasFeature) {
             return;
         }
@@ -2506,6 +2599,9 @@
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (parent) {
+                ap = ap.getParentActiveAdmin();
+            }
             if (ap.passwordQuality != quality) {
                 ap.passwordQuality = quality;
                 saveSettingsLocked(userHandle);
@@ -2514,35 +2610,67 @@
     }
 
     @Override
-    public int getPasswordQuality(ComponentName who, int userHandle) {
+    public int getPasswordQuality(ComponentName who, int userHandle, boolean parent) {
         if (!mHasFeature) {
             return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+                if (parent && admin != null) {
+                    admin = admin.getParentActiveAdmin();
+                }
                 return admin != null ? admin.passwordQuality : mode;
             }
 
-            // Return strictest policy for this user and profiles that are visible from this user.
-            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
-            for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+            if (isSeparateProfileChallengeEnabled(userHandle) && !parent) {
+                // If a Work Challenge is in use, only return its restrictions.
+                DevicePolicyData policy = getUserDataUnchecked(userHandle);
                 final int N = policy.mAdminList.size();
-                for (int i=0; i<N; i++) {
+                for (int i = 0; i < N; i++) {
                     ActiveAdmin admin = policy.mAdminList.get(i);
                     if (mode < admin.passwordQuality) {
                         mode = admin.passwordQuality;
                     }
                 }
+            } else {
+                // Return strictest policy for this user and profiles that are visible from this
+                // user that do not use a separate work challenge.
+                // TODO: When there are separate parent restrictions the profile should just
+                // obey its own.
+                List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+                for (UserInfo userInfo : profiles) {
+                    // Only aggregate data for the parent profile plus the non-work challenge
+                    // enabled profiles.
+                    if (!(userInfo.isManagedProfile()
+                            && isSeparateProfileChallengeEnabled(userInfo.id))) {
+                        DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+                        final int N = policy.mAdminList.size();
+                        for (int i = 0; i < N; i++) {
+                            ActiveAdmin admin = policy.mAdminList.get(i);
+                            if (mode < admin.passwordQuality) {
+                                mode = admin.passwordQuality;
+                            }
+                        }
+                    }
+                }
             }
             return mode;
         }
     }
 
+    private boolean isSeparateProfileChallengeEnabled(int userHandle) {
+        long ident = mInjector.binderClearCallingIdentity();
+        try {
+            return mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(ident);
+        }
+    }
+
     @Override
     public void setPasswordMinimumLength(ComponentName who, int length) {
         if (!mHasFeature) {
@@ -2565,7 +2693,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2612,7 +2740,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2672,7 +2800,7 @@
         if (!mHasFeature) {
             return 0L;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             long timeout = 0L;
 
@@ -2799,7 +2927,7 @@
         if (!mHasFeature) {
             return 0L;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             return getPasswordExpirationLocked(who, userHandle);
         }
@@ -2827,7 +2955,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2871,7 +2999,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2918,7 +3046,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -2968,7 +3096,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3018,7 +3146,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3068,7 +3196,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             int length = 0;
 
@@ -3097,11 +3225,11 @@
     }
 
     @Override
-    public boolean isActivePasswordSufficient(int userHandle) {
+    public boolean isActivePasswordSufficient(int userHandle, boolean parent) {
         if (!mHasFeature) {
             return true;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
 
         synchronized (this) {
             int id = getCredentialOwner(userHandle);
@@ -3109,8 +3237,14 @@
 
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
-            getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
-            if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle)
+            ActiveAdmin admin =
+                    getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            ComponentName adminComponentName = admin.info.getComponent();
+            // TODO: Include the Admin sdk level check in LockPatternUtils check.
+            ComponentName who = !isAdminApiLevelMOrBelow(adminComponentName, userHandle)
+                    && isSeparateProfileChallengeEnabled(userHandle)
+                        ? adminComponentName : null;
+            if (policy.mActivePasswordQuality < getPasswordQuality(who, userHandle, parent)
                     || policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
                 return false;
             }
@@ -3167,7 +3301,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle)
                     : getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
@@ -3180,7 +3314,7 @@
         if (!mHasFeature) {
             return UserHandle.USER_NULL;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
             return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL;
@@ -3275,7 +3409,7 @@
                     }
                 }
             }
-            quality = getPasswordQuality(null, userHandle);
+            quality = getPasswordQuality(null, userHandle, false);
             if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 int realQuality = LockPatternUtils.computePasswordQuality(password);
                 if (realQuality < quality
@@ -3479,7 +3613,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             long time = 0;
 
@@ -3572,7 +3706,7 @@
             }
 
             try {
-                int uid = mContext.getPackageManager().getPackageUid(
+                int uid = mContext.getPackageManager().getPackageUidAsUser(
                         policy.mDelegatedCertInstallerPackage, userHandle);
                 return uid == callingUid;
             } catch (NameNotFoundException e) {
@@ -3782,6 +3916,42 @@
         }
     }
 
+    @Override
+    public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage)
+            throws SecurityException {
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        }
+
+        final int userId = mInjector.userHandleGetCallingUserId();
+        final long token = mInjector.binderClearCallingIdentity();
+        try{
+            ConnectivityManager connectivityManager = (ConnectivityManager)
+                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+            return connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public String getAlwaysOnVpnPackage(ComponentName admin)
+            throws SecurityException {
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        }
+
+        final int userId = mInjector.userHandleGetCallingUserId();
+        final long token = mInjector.binderClearCallingIdentity();
+        try{
+            ConnectivityManager connectivityManager = (ConnectivityManager)
+                    mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+            return connectivityManager.getAlwaysOnVpnPackageForUser(userId);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(token);
+        }
+    }
+
     private void wipeDataLocked(boolean wipeExtRequested, String reason) {
         if (wipeExtRequested) {
             StorageManager sm = (StorageManager) mContext.getSystemService(
@@ -3801,7 +3971,7 @@
             return;
         }
         final int userHandle = mInjector.userHandleGetCallingUserId();
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
@@ -3880,7 +4050,7 @@
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
@@ -3909,9 +4079,9 @@
         if (!mHasFeature) {
             return;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         // Managed Profile password can only be changed when per user encryption is present.
-        if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+        if (!isSeparateProfileChallengeEnabled(userHandle)) {
             enforceNotManagedProfile(userHandle, "set the active password");
         }
 
@@ -3938,7 +4108,7 @@
                 setExpirationAlarmCheckLocked(mContext, policy);
 
                 // Send a broadcast to each profile using this password as its primary unlock.
-                if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
                     sendAdminCommandLocked(
                             DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                             DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
@@ -3978,7 +4148,7 @@
 
     @Override
     public void reportFailedPasswordAttempt(int userHandle) {
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         enforceNotManagedProfile(userHandle, "report failed password attempt");
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4020,7 +4190,7 @@
 
     @Override
     public void reportSuccessfulPasswordAttempt(int userHandle) {
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
@@ -4104,7 +4274,7 @@
         if (!mHasFeature) {
             return null;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized(this) {
             DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
             // Scan through active admins and find if anyone has already
@@ -4241,7 +4411,7 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             // Check for permissions if a particular caller is specified
             if (who != null) {
@@ -4271,7 +4441,7 @@
         if (!mHasFeature) {
             // Ok to return current status.
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         return getEncryptionStatus();
     }
 
@@ -4519,7 +4689,7 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (this) {
@@ -4530,10 +4700,9 @@
 
                 UserInfo user = mUserManager.getUserInfo(userHandle);
                 final List<UserInfo> profiles;
-                if (user.isManagedProfile() || LockPatternUtils.isSeparateWorkChallengeEnabled()) {
-                    // If we are being asked about a managed profile or the main user profile has a
-                    // separate lock from the work profile, just return keyguard features disabled
-                    // by admins in the profile.
+                if (user.isManagedProfile()) {
+                    // If we are being asked about a managed profile, just return keyguard features
+                    // disabled by admins in the profile.
                     profiles = Collections.singletonList(user);
                 } else {
                     // Otherwise return those set by admins in the user
@@ -4552,9 +4721,11 @@
                             // If we are being asked explictly about this user
                             // return all disabled features even if its a managed profile.
                             which |= admin.disabledKeyguardFeatures;
-                        } else {
+                        } else if (!isSeparateProfileChallengeEnabled(
+                                userInfo.id)) {
                             // Otherwise a managed profile is only allowed to disable
-                            // some features on the parent user.
+                            // some features on the parent user, and we only aggregate them if
+                            // it doesn't have its own challenge.
                             which |= (admin.disabledKeyguardFeatures
                                     & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER);
                         }
@@ -4732,7 +4903,7 @@
         Preconditions.checkNotNull(packageName, "packageName is null");
         final int callingUid = mInjector.binderGetCallingUid();
         try {
-            int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+            int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, 0);
             if (uid != callingUid) {
                 throw new SecurityException("Invalid packageName");
             }
@@ -5080,17 +5251,28 @@
         }
     }
 
-    private void enforceCrossUserPermission(int userHandle) {
+    private void enforceFullCrossUsersPermission(int userHandle) {
+        enforceSystemUserOrPermission(userHandle,
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    }
+
+    private void enforceCrossUsersPermission(int userHandle) {
+        enforceSystemUserOrPermission(userHandle,
+                android.Manifest.permission.INTERACT_ACROSS_USERS);
+    }
+
+    private void enforceSystemUserOrPermission(int userHandle, String permission) {
         if (userHandle < 0) {
             throw new IllegalArgumentException("Invalid userId " + userHandle);
         }
         final int callingUid = mInjector.binderGetCallingUid();
-        if (userHandle == UserHandle.getUserId(callingUid)) return;
+        if (userHandle == UserHandle.getUserId(callingUid)) {
+            return;
+        }
         if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
                 || callingUid == Process.ROOT_UID)) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
-                    + " INTERACT_ACROSS_USERS_FULL permission");
+            mContext.enforceCallingOrSelfPermission(permission,
+                    "Must be system or have " + permission + " permission");
         }
     }
 
@@ -5241,7 +5423,7 @@
             }
 
             try {
-                int uid = mContext.getPackageManager().getPackageUid(
+                int uid = mContext.getPackageManager().getPackageUidAsUser(
                         policy.mApplicationRestrictionsManagingPackage, userHandle);
                 return uid == callingUid;
             } catch (NameNotFoundException e) {
@@ -5300,7 +5482,7 @@
             return null;
         }
         Preconditions.checkNotNull(agent, "agent null");
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
 
         synchronized (this) {
             final String componentName = agent.flattenToString();
@@ -5963,7 +6145,7 @@
     @Override
     public Bundle getUserRestrictions(ComponentName who, int userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        enforceCrossUserPermission(userHandle);
+        enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
             ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(who, userHandle);
             if (activeAdmin == null) {
@@ -6155,7 +6337,7 @@
 
     @Override
     public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
-        enforceCrossUserPermission(userId);
+        enforceFullCrossUsersPermission(userId);
         if (!mHasFeature) {
             return null;
         }
@@ -6227,7 +6409,7 @@
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             if (admin.disableCallerId != disabled) {
                 admin.disableCallerId = disabled;
-                saveSettingsLocked(UserHandle.getCallingUserId());
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
             }
         }
     }
@@ -6247,8 +6429,7 @@
 
     @Override
     public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
-        // TODO: Should there be a check to make sure this relationship is within a profile group?
-        //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+        enforceCrossUsersPermission(userId);
         synchronized (this) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
             return (admin != null) ? admin.disableCallerId : false;
@@ -6256,6 +6437,44 @@
     }
 
     @Override
+    public void setCrossProfileContactsSearchDisabled(ComponentName who, boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (admin.disableContactsSearch != disabled) {
+                admin.disableContactsSearch = disabled;
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileContactsSearchDisabled(ComponentName who) {
+        if (!mHasFeature) {
+            return false;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return admin.disableContactsSearch;
+        }
+    }
+
+    @Override
+    public boolean getCrossProfileContactsSearchDisabledForUser(int userId) {
+        enforceCrossUsersPermission(userId);
+        synchronized (this) {
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
+            return (admin != null) ? admin.disableContactsSearch : false;
+        }
+    }
+
+    @Override
     public void startManagedQuickContact(String actualLookupKey, long actualContactId,
             long actualDirectoryId, Intent originalIntent) {
         final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
@@ -7104,4 +7323,99 @@
         }
     }
 
+    @Override
+    public void setShortSupportMessage(@NonNull ComponentName who, String message) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userHandle = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForUidLocked(who,
+                    mInjector.binderGetCallingUid());
+            if (!TextUtils.equals(admin.shortSupportMessage, message)) {
+                admin.shortSupportMessage = message;
+                saveSettingsLocked(userHandle);
+            }
+        }
+    }
+
+    @Override
+    public String getShortSupportMessage(@NonNull ComponentName who) {
+        if (!mHasFeature) {
+            return null;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForUidLocked(who,
+                    mInjector.binderGetCallingUid());
+            return admin.shortSupportMessage;
+        }
+    }
+
+    @Override
+    public void setLongSupportMessage(@NonNull ComponentName who, String message) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        final int userHandle = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForUidLocked(who,
+                    mInjector.binderGetCallingUid());
+            if (!TextUtils.equals(admin.longSupportMessage, message)) {
+                admin.longSupportMessage = message;
+                saveSettingsLocked(userHandle);
+            }
+        }
+    }
+
+    @Override
+    public String getLongSupportMessage(@NonNull ComponentName who) {
+        if (!mHasFeature) {
+            return null;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminForUidLocked(who,
+                    mInjector.binderGetCallingUid());
+            return admin.longSupportMessage;
+        }
+    }
+
+    @Override
+    public String getShortSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
+        if (!mHasFeature) {
+            return null;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+            throw new SecurityException("Only the system can query support message for user");
+        }
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+            if (admin != null) {
+                return admin.shortSupportMessage;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getLongSupportMessageForUser(@NonNull ComponentName who, int userHandle) {
+        if (!mHasFeature) {
+            return null;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
+            throw new SecurityException("Only the system can query support message for user");
+        }
+        synchronized (this) {
+            ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+            if (admin != null) {
+                return admin.longSupportMessage;
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0bbd23e..ee5f23f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -45,7 +45,6 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.view.WindowManager;
-import android.webkit.WebViewFactory;
 
 import com.android.internal.R;
 import com.android.internal.os.BinderInternal;
@@ -68,6 +67,7 @@
 import com.android.server.lights.LightsService;
 import com.android.server.media.MediaRouterService;
 import com.android.server.media.MediaSessionService;
+import com.android.server.media.MediaResourceMonitorService;
 import com.android.server.media.projection.MediaProjectionManagerService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
@@ -81,7 +81,6 @@
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
 import com.android.server.restrictions.RestrictionsManagerService;
-import com.android.server.search.SearchManagerService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
 import com.android.server.telecom.TelecomLoaderService;
@@ -130,6 +129,8 @@
             "com.android.server.midi.MidiService$Lifecycle";
     private static final String WIFI_SERVICE_CLASS =
             "com.android.server.wifi.WifiService";
+    private static final String WIFI_NAN_SERVICE_CLASS =
+            "com.android.server.wifi.nan.WifiNanService";
     private static final String WIFI_P2P_SERVICE_CLASS =
             "com.android.server.wifi.p2p.WifiP2pService";
     private static final String ETHERNET_SERVICE_CLASS =
@@ -138,6 +139,9 @@
             "com.android.server.job.JobSchedulerService";
     private static final String MOUNT_SERVICE_CLASS =
             "com.android.server.MountService$Lifecycle";
+    private static final String SEARCH_MANAGER_SERVICE_CLASS =
+            "com.android.server.search.SearchManagerService$Lifecycle";
+
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
     /**
@@ -736,6 +740,11 @@
                 }
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
+                if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)) {
+                    mSystemServiceManager.startService(WIFI_NAN_SERVICE_CLASS);
+                } else {
+                    Slog.i(TAG, "No Wi-Fi NAN Service (NAN support Not Present)");
+                }
                 mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                 mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
                 mSystemServiceManager.startService(
@@ -844,8 +853,7 @@
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartSearchManagerService");
                 try {
-                    ServiceManager.addService(Context.SEARCH_SERVICE,
-                            new SearchManagerService(context));
+                    mSystemServiceManager.startService(SEARCH_MANAGER_SERVICE_CLASS);
                 } catch (Throwable e) {
                     reportWtf("starting Search Service", e);
                 }
@@ -1025,6 +1033,8 @@
                 mSystemServiceManager.startService(TvInputManagerService.class);
             }
 
+            mSystemServiceManager.startService(MediaResourceMonitorService.class);
+
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartMediaRouterService");
                 try {
@@ -1322,7 +1332,7 @@
         Intent intent = new Intent();
         intent.setComponent(new ComponentName("com.android.systemui",
                     "com.android.systemui.SystemUIService"));
-        intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED);
+        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
         //Slog.d(TAG, "Starting service: " + intent);
         context.startServiceAsUser(intent, UserHandle.SYSTEM);
     }
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index d5f384d..5abb6e7 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.print;
 
+import static android.content.pm.PackageManager.GET_SERVICES;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -24,7 +27,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.drawable.Icon;
@@ -56,14 +58,12 @@
 import java.io.PrintWriter;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 
 /**
  * SystemService wrapper for the PrintManager implementation. Publishes
  * Context.PRINT_SERVICE.
  * PrintManager implementation is contained within.
  */
-
 public final class PrintManagerService extends SystemService {
     private final PrintManagerImpl mPrintManagerImpl;
 
@@ -88,11 +88,6 @@
     }
 
     class PrintManagerImpl extends IPrintManager.Stub {
-        private static final char COMPONENT_NAME_SEPARATOR = ':';
-
-        private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
-                "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
-
         private static final int BACKGROUND_USER_ID = -10;
 
         private final Object mLock = new Object();
@@ -487,7 +482,7 @@
 
         private void registerContentObservers() {
             final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
-                    Settings.Secure.ENABLED_PRINT_SERVICES);
+                    Settings.Secure.DISABLED_PRINT_SERVICES);
             ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
                 @Override
                 public void onChange(boolean selfChange, Uri uri, int userId) {
@@ -511,8 +506,7 @@
 
         private void registerBroadcastReceivers() {
             PackageMonitor monitor = new PackageMonitor() {
-                @Override
-                public void onPackageModified(String packageName) {
+                private void updateServices(String packageName) {
                     if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
                     synchronized (mLock) {
                         // A background user/profile's print jobs are running but there is
@@ -520,11 +514,15 @@
                         // to handle it as the change may affect ongoing print jobs.
                         boolean servicesChanged = false;
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-                        Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
-                        while (iterator.hasNext()) {
-                            ComponentName componentName = iterator.next();
-                            if (packageName.equals(componentName.getPackageName())) {
+
+                        List<PrintServiceInfo> installedServices = userState
+                                .getInstalledPrintServices();
+                        final int numInstalledServices = installedServices.size();
+                        for (int i = 0; i < numInstalledServices; i++) {
+                            if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
+                                    .equals(packageName)) {
                                 servicesChanged = true;
+                                break;
                             }
                         }
                         if (servicesChanged) {
@@ -534,30 +532,17 @@
                 }
 
                 @Override
+                public void onPackageModified(String packageName) {
+                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
+                    updateServices(packageName);
+                    getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
+                }
+
+                @Override
                 public void onPackageRemoved(String packageName, int uid) {
                     if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
-                    synchronized (mLock) {
-                        // A background user/profile's print jobs are running but there is
-                        // no UI shown. Hence, if the packages of such a user change we need
-                        // to handle it as the change may affect ongoing print jobs.
-                        boolean servicesRemoved = false;
-                        UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-                        Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
-                        while (iterator.hasNext()) {
-                            ComponentName componentName = iterator.next();
-                            if (packageName.equals(componentName.getPackageName())) {
-                                userState.removeApprovedPrintService(componentName);
-                                iterator.remove();
-                                servicesRemoved = true;
-                            }
-                        }
-                        if (servicesRemoved) {
-                            persistComponentNamesToSettingLocked(
-                                    Settings.Secure.ENABLED_PRINT_SERVICES,
-                                    userState.getEnabledServices(), getChangingUserId());
-                            userState.updateIfNeededLocked();
-                        }
-                    }
+                    updateServices(packageName);
+                    getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
                 }
 
                 @Override
@@ -570,10 +555,16 @@
                         // to handle it as the change may affect ongoing print jobs.
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
                         boolean stoppedSomePackages = false;
-                        Iterator<ComponentName> iterator = userState.getEnabledServices()
-                                .iterator();
+
+                        List<PrintServiceInfo> enabledServices = userState
+                                .getEnabledPrintServices();
+                        if (enabledServices == null) {
+                            return false;
+                        }
+
+                        Iterator<PrintServiceInfo> iterator = enabledServices.iterator();
                         while (iterator.hasNext()) {
-                            ComponentName componentName = iterator.next();
+                            ComponentName componentName = iterator.next().getComponentName();
                             String componentPackage = componentName.getPackageName();
                             for (String stoppedPackage : stoppedPackages) {
                                 if (componentPackage.equals(stoppedPackage)) {
@@ -603,51 +594,15 @@
                     intent.setPackage(packageName);
 
                     List<ResolveInfo> installedServices = mContext.getPackageManager()
-                            .queryIntentServicesAsUser(intent, PackageManager.GET_SERVICES,
+                            .queryIntentServicesAsUser(intent,
+                                    GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING,
                                     getChangingUserId());
 
-                    if (installedServices == null) {
-                        return;
-                    }
-
-                    // Enable all added services by default
-                    synchronized (mLock) {
+                    if (installedServices != null) {
                         UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-
-                        Set<ComponentName> enabledServices = userState.getEnabledServices();
-                        boolean servicesAdded = false;
-
-                        final int installedServiceCount = installedServices.size();
-                        for (int i = 0; i < installedServiceCount; i++) {
-                            ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
-                            ComponentName component = new ComponentName(serviceInfo.packageName,
-                                    serviceInfo.name);
-
-                            enabledServices.add(component);
-                            servicesAdded = true;
-                        }
-
-                        if (servicesAdded) {
-                            persistComponentNamesToSettingLocked(
-                                    Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices,
-                                    getChangingUserId());
-                            userState.updateIfNeededLocked();
-                        }
+                        userState.updateIfNeededLocked();
                     }
                 }
-
-                private void persistComponentNamesToSettingLocked(String settingName,
-                        Set<ComponentName> componentNames, int userId) {
-                    StringBuilder builder = new StringBuilder();
-                    for (ComponentName componentName : componentNames) {
-                        if (builder.length() > 0) {
-                            builder.append(COMPONENT_NAME_SEPARATOR);
-                        }
-                        builder.append(componentName.flattenToShortString());
-                    }
-                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                            settingName, builder.toString(), userId);
-                }
             };
 
             // package changes
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 0af1525..5ee4066 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -863,8 +863,7 @@
         }
 
         private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) {
-            if (printerId == null || printerId.getServiceName() == null
-                    || !printerId.getServiceName().equals(serviceName)) {
+            if (printerId == null || !printerId.getServiceName().equals(serviceName)) {
                 throw new IllegalArgumentException("Invalid printer id: " + printerId);
             }
         }
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index 40a8880..d179b95 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -37,17 +37,18 @@
 import android.print.PrintJobId;
 import android.print.PrintJobInfo;
 import android.print.PrinterId;
+import android.printservice.PrintService;
 import android.util.Slog;
 import android.util.TimedRemoteCaller;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
-import libcore.io.IoUtils;
-
 /**
  * This represents the remote print spooler as a local object to the
  * PrintManagerService. It is responsible to connecting to the remote
@@ -439,26 +440,24 @@
     }
 
     /**
-     * Connect to the print spooler service and remove an approved print service.
+     * Remove all approved {@link PrintService print services} that are not in the given set.
      *
-     * @param serviceToRemove The {@link ComponentName} of the service to be removed.
+     * @param servicesToKeep The {@link ComponentName names } of the services to keep
      */
-    public final void removeApprovedPrintService(ComponentName serviceToRemove) {
+    public final void pruneApprovedPrintServices(List<ComponentName> servicesToKeep) {
         throwIfCalledOnMainThread();
         synchronized (mLock) {
             throwIfDestroyedLocked();
             mCanUnbind = false;
         }
         try {
-            getRemoteInstanceLazy().removeApprovedPrintService(serviceToRemove);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error removing approved print service.", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error removing approved print service.", te);
+            getRemoteInstanceLazy().pruneApprovedPrintServices(servicesToKeep);
+        } catch (RemoteException|TimeoutException re) {
+            Slog.e(LOG_TAG, "Error pruning approved print services.", re);
         } finally {
             if (DEBUG) {
                 Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
-                        + "] removing approved print service()");
+                        + "] pruneApprovedPrintServices()");
             }
             synchronized (mLock) {
                 mCanUnbind = true;
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 63d3301..78edc4d 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -16,16 +16,18 @@
 
 package com.android.server.print;
 
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_SERVICES;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -98,7 +100,7 @@
     private final List<PrintServiceInfo> mInstalledServices =
             new ArrayList<PrintServiceInfo>();
 
-    private final Set<ComponentName> mEnabledServices =
+    private final Set<ComponentName> mDisabledServices =
             new ArraySet<ComponentName>();
 
     private final PrintJobForAppCache mPrintJobForAppCache =
@@ -126,8 +128,15 @@
         mLock = lock;
         mSpooler = new RemotePrintSpooler(context, userId, this);
         mHandler = new UserStateHandler(context.getMainLooper());
+
         synchronized (mLock) {
-            enableSystemPrintServicesLocked();
+            readInstalledPrintServicesLocked();
+            upgradePersistentStateIfNeeded();
+            readDisabledPrintServicesLocked();
+
+            // Some print services might have gotten installed before the User State came up
+            prunePrintServices();
+
             onConfigurationChangedLocked();
         }
     }
@@ -320,15 +329,6 @@
         }
     }
 
-    /**
-     * Remove an approved print service.
-     *
-     * @param serviceToRemove The {@link ComponentName} of the service to be removed.
-     */
-    public void removeApprovedPrintService(ComponentName serviceToRemove) {
-        mSpooler.removeApprovedPrintService(serviceToRemove);
-    }
-
     public void restartPrintJob(PrintJobId printJobId, int appId) {
         PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, appId);
         if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
@@ -337,7 +337,7 @@
         mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
     }
 
-    public List<PrintServiceInfo> getEnabledPrintServices() {
+    public @Nullable List<PrintServiceInfo> getEnabledPrintServices() {
         synchronized (mLock) {
             List<PrintServiceInfo> enabledServices = null;
             final int installedServiceCount = mInstalledServices.size();
@@ -597,13 +597,6 @@
         }
     }
 
-    public Set<ComponentName> getEnabledServices() {
-        synchronized(mLock) {
-            throwIfDestroyedLocked();
-            return mEnabledServices;
-        }
-    }
-
     public void destroyLocked() {
         throwIfDestroyedLocked();
         mSpooler.destroy();
@@ -612,7 +605,7 @@
         }
         mActiveServices.clear();
         mInstalledServices.clear();
-        mEnabledServices.clear();
+        mDisabledServices.clear();
         if (mPrinterDiscoverySession != null) {
             mPrinterDiscoverySession.destroyLocked();
             mPrinterDiscoverySession = null;
@@ -646,12 +639,12 @@
                    .append(installedService.getAdvancedOptionsActivityName()).println();
         }
 
-        pw.append(prefix).append(tab).append("enabled services:").println();
-        for (ComponentName enabledService : mEnabledServices) {
-            String enabledServicePrefix = prefix + tab + tab;
-            pw.append(enabledServicePrefix).append("service:").println();
-            pw.append(enabledServicePrefix).append(tab).append("componentName=")
-                    .append(enabledService.flattenToString());
+        pw.append(prefix).append(tab).append("disabled services:").println();
+        for (ComponentName disabledService : mDisabledServices) {
+            String disabledServicePrefix = prefix + tab + tab;
+            pw.append(disabledServicePrefix).append("service:").println();
+            pw.append(disabledServicePrefix).append(tab).append("componentName=")
+                    .append(disabledService.flattenToString());
             pw.println();
         }
 
@@ -679,7 +672,7 @@
     private boolean readConfigurationLocked() {
         boolean somethingChanged = false;
         somethingChanged |= readInstalledPrintServicesLocked();
-        somethingChanged |= readEnabledPrintServicesLocked();
+        somethingChanged |= readDisabledPrintServicesLocked();
         return somethingChanged;
     }
 
@@ -687,8 +680,8 @@
         Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
 
         List<ResolveInfo> installedServices = mContext.getPackageManager()
-                .queryIntentServicesAsUser(mQueryIntent, PackageManager.GET_SERVICES
-                        | PackageManager.GET_META_DATA, mUserId);
+                .queryIntentServicesAsUser(mQueryIntent,
+                        GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId);
 
         final int installedCount = installedServices.size();
         for (int i = 0, count = installedCount; i < count; i++) {
@@ -742,13 +735,50 @@
         return false;
     }
 
-    private boolean readEnabledPrintServicesLocked() {
-        Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
-        readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
-                tempEnabledServiceNameSet);
-        if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
-            mEnabledServices.clear();
-            mEnabledServices.addAll(tempEnabledServiceNameSet);
+    /**
+     * Update persistent state from a previous version of Android.
+     */
+    private void upgradePersistentStateIfNeeded() {
+        String enabledSettingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
+
+        // Pre N we store the enabled services, in N and later we store the disabled services.
+        // Hence if enabledSettingValue is still set, we need to upgrade.
+        if (enabledSettingValue != null) {
+            Set<ComponentName> enabledServiceNameSet = new HashSet<ComponentName>();
+            readPrintServicesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
+                    enabledServiceNameSet);
+
+            ArraySet<ComponentName> disabledServices = new ArraySet<>();
+            final int numInstalledServices = mInstalledServices.size();
+            for (int i = 0; i < numInstalledServices; i++) {
+                ComponentName serviceName = mInstalledServices.get(i).getComponentName();
+                if (!enabledServiceNameSet.contains(serviceName)) {
+                    disabledServices.add(serviceName);
+                }
+            }
+
+            writeDisabledPrintServicesLocked(disabledServices);
+
+            // We won't needed ENABLED_PRINT_SERVICES anymore, set to null to prevent upgrade to run
+            // again.
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ENABLED_PRINT_SERVICES, null, mUserId);
+        }
+    }
+
+    /**
+     * Read the set of disabled print services from the secure settings.
+     *
+     * @return true if the state changed.
+     */
+    private boolean readDisabledPrintServicesLocked() {
+        Set<ComponentName> tempDisabledServiceNameSet = new HashSet<ComponentName>();
+        readPrintServicesFromSettingLocked(Settings.Secure.DISABLED_PRINT_SERVICES,
+                tempDisabledServiceNameSet);
+        if (!tempDisabledServiceNameSet.equals(mDisabledServices)) {
+            mDisabledServices.clear();
+            mDisabledServices.addAll(tempDisabledServiceNameSet);
             return true;
         }
         return false;
@@ -774,70 +804,28 @@
         }
     }
 
-    private void enableSystemPrintServicesLocked() {
-        // Load enabled and installed services.
-        readEnabledPrintServicesLocked();
-        readInstalledPrintServicesLocked();
-
-        // Load the system services once enabled on first boot.
-        Set<ComponentName> enabledOnFirstBoot = new HashSet<ComponentName>();
-        readPrintServicesFromSettingLocked(
-                Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
-                enabledOnFirstBoot);
-
+    /**
+     * Persist the disabled print services to the secure settings.
+     */
+    private void writeDisabledPrintServicesLocked(Set<ComponentName> disabledServices) {
         StringBuilder builder = new StringBuilder();
-
-        final int serviceCount = mInstalledServices.size();
-        for (int i = 0; i < serviceCount; i++) {
-            ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo;
-            // Enable system print services if we never did that and are not enabled.
-            if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                ComponentName serviceName = new ComponentName(
-                        serviceInfo.packageName, serviceInfo.name);
-                if (!mEnabledServices.contains(serviceName)
-                        && !enabledOnFirstBoot.contains(serviceName)) {
-                    if (builder.length() > 0) {
-                        builder.append(":");
-                    }
-                    builder.append(serviceName.flattenToString());
-                }
+        for (ComponentName componentName : disabledServices) {
+            if (builder.length() > 0) {
+                builder.append(COMPONENT_NAME_SEPARATOR);
             }
-        }
-
-        // Nothing to be enabled - done.
-        if (builder.length() <= 0) {
-            return;
-        }
-
-        String servicesToEnable = builder.toString();
-
-        // Update the enabled services setting.
-        String enabledServices = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
-        if (TextUtils.isEmpty(enabledServices)) {
-            enabledServices = servicesToEnable;
-        } else {
-            enabledServices = enabledServices + ":" + servicesToEnable;
+            builder.append(componentName.flattenToShortString());
         }
         Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                Settings.Secure.ENABLED_PRINT_SERVICES, enabledServices, mUserId);
-
-        // Update the enabled on first boot services setting.
-        String enabledOnFirstBootServices = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES, mUserId);
-        if (TextUtils.isEmpty(enabledOnFirstBootServices)) {
-            enabledOnFirstBootServices = servicesToEnable;
-        } else {
-            enabledOnFirstBootServices = enabledOnFirstBootServices + ":" + enabledServices;
-        }
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                Settings.Secure.ENABLED_ON_FIRST_BOOT_SYSTEM_PRINT_SERVICES,
-                enabledOnFirstBootServices, mUserId);
+                Settings.Secure.DISABLED_PRINT_SERVICES, builder.toString(), mUserId);
     }
 
-    private void onConfigurationChangedLocked() {
-        Set<ComponentName> installedComponents = new ArraySet<ComponentName>();
+    /**
+     * Get the {@link ComponentName names} of the installed print services
+     *
+     * @return The names of the installed print services
+     */
+    private ArrayList<ComponentName> getInstalledComponents() {
+        ArrayList<ComponentName> installedComponents = new ArrayList<ComponentName>();
 
         final int installedCount = mInstalledServices.size();
         for (int i = 0; i < installedCount; i++) {
@@ -846,8 +834,37 @@
                     resolveInfo.serviceInfo.name);
 
             installedComponents.add(serviceName);
+        }
 
-            if (mEnabledServices.contains(serviceName)) {
+        return installedComponents;
+    }
+
+    /**
+     * Prune persistent state if a print service was uninstalled
+     */
+    public void prunePrintServices() {
+        synchronized (mLock) {
+            ArrayList<ComponentName> installedComponents = getInstalledComponents();
+
+            // Remove unnecessary entries from persistent state "disabled services"
+            boolean disabledServicesUninstalled = mDisabledServices.retainAll(installedComponents);
+            if (disabledServicesUninstalled) {
+                writeDisabledPrintServicesLocked(mDisabledServices);
+            }
+
+            // Remove unnecessary entries from persistent state "approved services"
+            mSpooler.pruneApprovedPrintServices(installedComponents);
+        }
+    }
+
+    private void onConfigurationChangedLocked() {
+        ArrayList<ComponentName> installedComponents = getInstalledComponents();
+
+        final int installedCount = installedComponents.size();
+        for (int i = 0; i < installedCount; i++) {
+            ComponentName serviceName = installedComponents.get(i);
+
+            if (!mDisabledServices.contains(serviceName)) {
                 if (!mActiveServices.containsKey(serviceName)) {
                     RemotePrintService service = new RemotePrintService(
                             mContext, serviceName, mUserId, mSpooler, this);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index eed326e..3ae1072 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -27,7 +27,9 @@
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
-
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+    <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
     <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
@@ -102,6 +104,8 @@
             </intent-filter>
         </receiver>
 
+        <service android:name="com.android.server.job.MockPriorityJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" />
     </application>
 
     <instrumentation
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 27deb72..27d5207 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -59,6 +59,7 @@
 import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.LogPrinter;
 
@@ -1504,4 +1505,10 @@
         ka3.stop();
         callback3.expectStopped();
     }
+
+    @SmallTest
+    public void testGetCaptivePortalServerUrl() throws Exception {
+        String url = mCm.getCaptivePortalServerUrl();
+        assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
new file mode 100644
index 0000000..625fe77
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.server.am;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+public class ActivityManagerTest extends AndroidTestCase {
+
+    IActivityManager service;
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        service = ActivityManagerNative.getDefault();
+    }
+
+    public void testTaskIdsForRunningUsers() throws RemoteException {
+        for(int userId : service.getRunningUserIds()) {
+            testTaskIdsForUser(userId);
+        }
+    }
+
+    private void testTaskIdsForUser(int userId) throws RemoteException {
+        List<ActivityManager.RecentTaskInfo> recentTasks = service.getRecentTasks(
+                100, 0, userId);
+        if(recentTasks != null) {
+            for(ActivityManager.RecentTaskInfo recentTask : recentTasks) {
+                int taskId = recentTask.persistentId;
+                assertEquals("The task id " + taskId + " should not belong to user " + userId,
+                        taskId / UserHandle.PER_USER_RANGE, userId);
+            }
+        }
+    }
+}
\ No newline at end of file
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 568e1d5..672058b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -758,7 +758,7 @@
         when(mContext.userManager.getUserRestrictions()).thenReturn(new Bundle());
 
         // Now call clear.
-        doReturn(DpmMockContext.CALLER_SYSTEM_USER_UID).when(mContext.packageManager).getPackageUid(
+        doReturn(DpmMockContext.CALLER_SYSTEM_USER_UID).when(mContext.packageManager).getPackageUidAsUser(
                 eq(admin1.getPackageName()),
                 anyInt());
         dpm.clearDeviceOwnerApp(admin1.getPackageName());
@@ -797,7 +797,7 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
 
         // Now call clear.
-        doReturn(DpmMockContext.CALLER_UID).when(mContext.packageManager).getPackageUid(
+        doReturn(DpmMockContext.CALLER_UID).when(mContext.packageManager).getPackageUidAsUser(
                 eq(admin1.getPackageName()),
                 anyInt());
         try {
@@ -951,7 +951,7 @@
         final int appRestrictionsManagerAppId = 20987;
         final int appRestrictionsManagerUid = UserHandle.getUid(
                 DpmMockContext.CALLER_USER_HANDLE, appRestrictionsManagerAppId);
-        doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUid(
+        doReturn(appRestrictionsManagerUid).when(mContext.packageManager).getPackageUidAsUser(
                 eq(appRestrictionsManagerPackage),
                 eq(DpmMockContext.CALLER_USER_HANDLE));
         mContext.binder.callingUid = appRestrictionsManagerUid;
@@ -1385,4 +1385,95 @@
 
         dpm.reboot(admin1);
     }
+
+    public void testSetGetSupportText() {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        dpm.setActiveAdmin(admin1, true);
+        dpm.setActiveAdmin(admin2, true);
+        mContext.callerPermissions.remove(permission.MANAGE_DEVICE_ADMINS);
+
+        // Null default support messages.
+        {
+            assertNull(dpm.getLongSupportMessage(admin1));
+            assertNull(dpm.getShortSupportMessage(admin1));
+            mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+            assertNull(dpm.getShortSupportMessageForUser(admin1,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            assertNull(dpm.getLongSupportMessageForUser(admin1,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        }
+
+        // Only system can call the per user versions.
+        {
+            try {
+                dpm.getShortSupportMessageForUser(admin1,
+                        DpmMockContext.CALLER_USER_HANDLE);
+                fail("Only system should be able to call getXXXForUser versions");
+            } catch (SecurityException expected) {
+                MoreAsserts.assertContainsRegex("message for user", expected.getMessage());
+            }
+            try {
+                dpm.getLongSupportMessageForUser(admin1,
+                        DpmMockContext.CALLER_USER_HANDLE);
+                fail("Only system should be able to call getXXXForUser versions");
+            } catch (SecurityException expected) {
+                MoreAsserts.assertContainsRegex("message for user", expected.getMessage());
+            }
+        }
+
+        // Can't set message for admin in another uid.
+        {
+            mContext.binder.callingUid = DpmMockContext.CALLER_UID + 1;
+            try {
+                dpm.setShortSupportMessage(admin1, "Some text");
+                fail("Admins should only be able to change their own support text.");
+            } catch (SecurityException expected) {
+                MoreAsserts.assertContainsRegex("is not owned by uid", expected.getMessage());
+            }
+            mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        }
+
+        // Set/Get short returns what it sets and other admins text isn't changed.
+        {
+            final String supportText = "Some text to test with.";
+            dpm.setShortSupportMessage(admin1, supportText);
+            assertEquals(supportText, dpm.getShortSupportMessage(admin1));
+            assertNull(dpm.getLongSupportMessage(admin1));
+            assertNull(dpm.getShortSupportMessage(admin2));
+
+            mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+            assertEquals(supportText, dpm.getShortSupportMessageForUser(admin1,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            assertNull(dpm.getShortSupportMessageForUser(admin2,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            assertNull(dpm.getLongSupportMessageForUser(admin1,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+            dpm.setShortSupportMessage(admin1, null);
+            assertNull(dpm.getShortSupportMessage(admin1));
+        }
+
+        // Set/Get long returns what it sets and other admins text isn't changed.
+        {
+            final String supportText = "Some text to test with.\nWith more text.";
+            dpm.setLongSupportMessage(admin1, supportText);
+            assertEquals(supportText, dpm.getLongSupportMessage(admin1));
+            assertNull(dpm.getShortSupportMessage(admin1));
+            assertNull(dpm.getLongSupportMessage(admin2));
+
+            mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+            assertEquals(supportText, dpm.getLongSupportMessageForUser(admin1,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            assertNull(dpm.getLongSupportMessageForUser(admin2,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            assertNull(dpm.getShortSupportMessageForUser(admin1,
+                    DpmMockContext.CALLER_USER_HANDLE));
+            mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+            dpm.setLongSupportMessage(admin1, null);
+            assertNull(dpm.getLongSupportMessage(admin1));
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 5b33e4d..c557ab7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -117,7 +117,7 @@
         // We need to rewrite the UID in the activity info.
         realResolveInfo.get(0).activityInfo.applicationInfo = ai;
 
-        doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceivers(
+        doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceiversAsUser(
                 MockUtils.checkIntentComponent(admin),
                 eq(PackageManager.GET_META_DATA
                         | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 312b1b0..ae0a25e 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -179,7 +179,21 @@
                 loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS);
         // Assert late runtime was clamped to be now + period*2.
         assertTrue("Early runtime wasn't correctly clamped.",
-                loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS*2);
+                loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS * 2);
+    }
+
+    public void testPriorityPersisted() throws Exception {
+        JobInfo.Builder b = new Builder(92, mComponent)
+                .setOverrideDeadline(5000)
+                .setPriority(42)
+                .setPersisted(true);
+        final JobStatus js = new JobStatus(b.build(), SOME_UID);
+        mTaskStoreUnderTest.add(js);
+        Thread.sleep(IO_WAIT);
+        final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+        JobStatus loaded = jobStatusSet.iterator().next();
+        assertEquals("Priority not correctly persisted.", 42, loaded.getPriority());
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java b/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
new file mode 100644
index 0000000..3ea86f2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+@TargetApi(24)
+public class MockPriorityJobService extends JobService {
+    private static final String TAG = "MockPriorityJobService";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.e(TAG, "Created test service.");
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        Log.i(TAG, "Test job executing: " + params.getJobId());
+        TestEnvironment.getTestEnvironment().executedEvents.add(
+                new TestEnvironment.Event(TestEnvironment.EVENT_START_JOB, params.getJobId()));
+        return true;  // Job not finished
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        Log.i(TAG, "Test job stop executing: " + params.getJobId());
+        int reason = params.getStopReason();
+        int event = TestEnvironment.EVENT_STOP_JOB;
+        Log.d(TAG, "stop reason: " + String.valueOf(reason));
+        if (reason == JobParameters.REASON_PREEMPT) {
+            event = TestEnvironment.EVENT_PREEMPT_JOB;
+            Log.d(TAG, "preempted " + String.valueOf(params.getJobId()));
+        }
+        TestEnvironment.getTestEnvironment().executedEvents
+                .add(new TestEnvironment.Event(event, params.getJobId()));
+        return false;  // Do not reschedule
+    }
+
+    public static class TestEnvironment {
+
+        public static final int EVENT_START_JOB = 0;
+        public static final int EVENT_PREEMPT_JOB = 1;
+        public static final int EVENT_STOP_JOB = 2;
+
+        private static TestEnvironment kTestEnvironment;
+
+        private ArrayList<Event> executedEvents = new ArrayList<Event>();
+
+        public static TestEnvironment getTestEnvironment() {
+            if (kTestEnvironment == null) {
+                kTestEnvironment = new TestEnvironment();
+            }
+            return kTestEnvironment;
+        }
+
+        public static class Event {
+            public int event;
+            public int jobId;
+
+            public Event() {
+            }
+
+            public Event(int event, int jobId) {
+                this.event = event;
+                this.jobId = jobId;
+            }
+
+            @Override
+            public boolean equals(Object other) {
+                if (other instanceof Event) {
+                    Event otherEvent = (Event) other;
+                    return otherEvent.event == event && otherEvent.jobId == jobId;
+                }
+                return false;
+            }
+        }
+
+        public void setUp() {
+            executedEvents.clear();
+        }
+
+        public ArrayList<Event> getExecutedEvents() {
+            return executedEvents;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/PrioritySchedulingTest.java b/services/tests/servicestests/src/com/android/server/job/PrioritySchedulingTest.java
new file mode 100644
index 0000000..63bccfa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/PrioritySchedulingTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import com.android.server.job.MockPriorityJobService.TestEnvironment;
+import com.android.server.job.MockPriorityJobService.TestEnvironment.Event;
+
+import java.util.ArrayList;
+
+@TargetApi(24)
+public class PrioritySchedulingTest extends AndroidTestCase {
+    /** Environment that notifies of JobScheduler callbacks. */
+    static TestEnvironment kTestEnvironment = TestEnvironment.getTestEnvironment();
+    /** Handle for the service which receives the execution callbacks from the JobScheduler. */
+    static ComponentName kJobServiceComponent;
+    JobScheduler mJobScheduler;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        kTestEnvironment.setUp();
+        kJobServiceComponent = new ComponentName(getContext(), MockPriorityJobService.class);
+        mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        mJobScheduler.cancelAll();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mJobScheduler.cancelAll();
+        super.tearDown();
+    }
+
+    public void testLowerPriorityJobPreempted() throws Exception {
+        JobInfo job1 = new JobInfo.Builder(111, kJobServiceComponent)
+                .setPriority(1)
+                .setOverrideDeadline(7000L)
+                .build();
+        JobInfo job2 = new JobInfo.Builder(222, kJobServiceComponent)
+                .setPriority(1)
+                .setOverrideDeadline(7000L)
+                .build();
+        JobInfo job3 = new JobInfo.Builder(333, kJobServiceComponent)
+                .setPriority(1)
+                .setOverrideDeadline(7000L)
+                .build();
+        JobInfo job4 = new JobInfo.Builder(444, kJobServiceComponent)
+                .setPriority(2)
+                .setMinimumLatency(2000L)
+                .setOverrideDeadline(7000L)
+                .build();
+        mJobScheduler.schedule(job1);
+        mJobScheduler.schedule(job2);
+        mJobScheduler.schedule(job3);
+        mJobScheduler.schedule(job4);
+        Thread.sleep(10000);  // Wait for job 4 to preempt one of the lower priority jobs
+
+        Event job4Execution = new Event(TestEnvironment.EVENT_START_JOB, 444);
+        ArrayList<Event> executedEvents = kTestEnvironment.getExecutedEvents();
+        boolean wasJob4Executed = executedEvents.contains(job4Execution);
+        boolean wasSomeJobPreempted = false;
+        for (Event event: executedEvents) {
+            if (event.event == TestEnvironment.EVENT_PREEMPT_JOB) {
+                wasSomeJobPreempted = true;
+                break;
+            }
+        }
+        assertTrue("No job was preempted.", wasSomeJobPreempted);
+        assertTrue("Lower priority jobs were not preempted.",  wasJob4Executed);
+    }
+
+    public void testHigherPriorityJobNotPreempted() throws Exception {
+        JobInfo job1 = new JobInfo.Builder(111, kJobServiceComponent)
+                .setPriority(2)
+                .setOverrideDeadline(7000L)
+                .build();
+        JobInfo job2 = new JobInfo.Builder(222, kJobServiceComponent)
+                .setPriority(2)
+                .setOverrideDeadline(7000L)
+                .build();
+        JobInfo job3 = new JobInfo.Builder(333, kJobServiceComponent)
+                .setPriority(2)
+                .setOverrideDeadline(7000L)
+                .build();
+        JobInfo job4 = new JobInfo.Builder(444, kJobServiceComponent)
+                .setPriority(1)
+                .setMinimumLatency(2000L)
+                .setOverrideDeadline(7000L)
+                .build();
+        mJobScheduler.schedule(job1);
+        mJobScheduler.schedule(job2);
+        mJobScheduler.schedule(job3);
+        mJobScheduler.schedule(job4);
+        Thread.sleep(10000);  // Wait for job 4 to preempt one of the higher priority jobs
+
+        Event job4Execution = new Event(TestEnvironment.EVENT_START_JOB, 444);
+        boolean wasJob4Executed = kTestEnvironment.getExecutedEvents().contains(job4Execution);
+        assertFalse("Higher priority job was preempted.", wasJob4Executed);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index df7b412..2640889 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -35,7 +35,7 @@
 
 public class RankingHelperTest extends AndroidTestCase {
     @Mock NotificationUsageStats mUsageStats;
-    @Mock Handler handler;
+    @Mock RankingHandler handler;
 
     private Notification mNotiGroupGSortA;
     private Notification mNotiGroupGSortB;
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index 79b9135..0f9bf2f 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -72,7 +72,7 @@
     public void testNonSearchable() {
         // test basic array & hashmap
         Searchables searchables = new Searchables(mContext, 0);
-        searchables.buildSearchableList();
+        searchables.updateSearchableList();
 
         // confirm that we return null for non-searchy activities
         ComponentName nonActivity = new ComponentName(
@@ -104,7 +104,7 @@
         // build item list with real-world source data
         mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
         Searchables searchables = new Searchables(mockContext, 0);
-        searchables.buildSearchableList();
+        searchables.updateSearchableList();
         // tests with "real" searchables (deprecate, this should be a unit test)
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         int count = searchablesList.size();
@@ -123,7 +123,7 @@
 
         mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
         Searchables searchables = new Searchables(mockContext, 0);
-        searchables.buildSearchableList();
+        searchables.updateSearchableList();
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         assertNotNull(searchablesList);
         MoreAsserts.assertEmpty(searchablesList);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2b0919b..4253cd4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -280,6 +280,11 @@
         mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
     }
 
+    @Override
+    public long getAppIdleRollingWindowDurationMillis() {
+        return mAppIdleWallclockThresholdMillis * 2;
+    }
+
     private void cleanUpRemovedUsersLocked() {
         final List<UserInfo> users = mUserManager.getUsers(true);
         if (users == null || users.size() == 0) {
@@ -374,7 +379,7 @@
         for (int i = 0; i < userIds.length; i++) {
             final int userId = userIds[i];
             List<PackageInfo> packages =
-                    getContext().getPackageManager().getInstalledPackages(
+                    getContext().getPackageManager().getInstalledPackagesAsUser(
                             PackageManager.GET_DISABLED_COMPONENTS
                                 | PackageManager.GET_UNINSTALLED_PACKAGES,
                             userId);
@@ -418,7 +423,8 @@
 
     private void notifyBatteryStats(String packageName, int userId, boolean idle) {
         try {
-            int uid = AppGlobals.getPackageManager().getPackageUid(packageName, userId);
+            final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
             if (idle) {
                 mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
                         packageName, uid);
@@ -547,7 +553,8 @@
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
                 final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime, resetBeginIdleTime);
+                service.onTimeChanged(expectedSystemTime, actualSystemTime, mScreenOnTime,
+                        resetBeginIdleTime);
             }
             mRealTimeSnapshot = actualRealtime;
             mSystemTimeSnapshot = actualSystemTime;
@@ -614,9 +621,8 @@
             // the sync adapter is a system package.
             try {
                 PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(
-                        packageName, 0, userId);
-                if (pi == null || pi.applicationInfo == null
-                        || !pi.applicationInfo.isSystemApp()) {
+                        packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+                if (pi == null || pi.applicationInfo == null) {
                     continue;
                 }
                 if (!packageName.equals(providerPkgName)) {
@@ -863,8 +869,8 @@
 
         List<ApplicationInfo> apps;
         try {
-            ParceledListSlice<ApplicationInfo> slice
-                    = AppGlobals.getPackageManager().getInstalledApplications(0, userId);
+            ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+                    .getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
             if (slice == null) {
                 return new int[0];
             }
@@ -1106,7 +1112,13 @@
      * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
      */
     private class SettingsObserver extends ContentObserver {
-        private static final String KEY_IDLE_DURATION = "idle_duration";
+        /**
+         * This flag has been used to disable app idle on older builds with bug b/26355386.
+         */
+        @Deprecated
+        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+        private static final String KEY_IDLE_DURATION = "idle_duration2";
         private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
         private static final String KEY_PAROLE_INTERVAL = "parole_interval";
         private static final String KEY_PAROLE_DURATION = "parole_duration";
@@ -1266,8 +1278,8 @@
                     "No permission to change app idle state");
             final long token = Binder.clearCallingIdentity();
             try {
-                PackageInfo pi = AppGlobals.getPackageManager()
-                        .getPackageInfo(packageName, 0, userId);
+                PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo(packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
                 if (pi == null) return;
                 UsageStatsService.this.setAppIdle(packageName, idle, userId);
             } catch (RemoteException re) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 5188e5f..224faf4 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,6 +59,7 @@
     private final Context mContext;
     private final UsageStatsDatabase mDatabase;
     private final IntervalStats[] mCurrentStats;
+    private IntervalStats mAppIdleRollingWindow;
     private boolean mStatsChanged = false;
     private final UnixCalendar mDailyExpiryDate;
     private final StatsUpdatedListener mListener;
@@ -72,6 +73,7 @@
 
     interface StatsUpdatedListener {
         void onStatsUpdated();
+        long getAppIdleRollingWindowDurationMillis();
     }
 
     UserUsageStatsService(Context context, int userId, File usageStatsDir,
@@ -133,6 +135,8 @@
             stat.updateConfigurationStats(null, stat.lastTimeSaved);
         }
 
+        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
+
         if (mDatabase.isNewUpdate()) {
             initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
                     mDatabase.isFirstUpdate());
@@ -148,7 +152,7 @@
     private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
             boolean firstUpdate) {
         PackageManager pm = mContext.getPackageManager();
-        List<PackageInfo> packages = pm.getInstalledPackages(0, mUserId);
+        List<PackageInfo> packages = pm.getInstalledPackagesAsUser(0, mUserId);
         final int packageCount = packages.size();
         for (int i = 0; i < packageCount; i++) {
             final PackageInfo pi = packages.get(i);
@@ -158,18 +162,24 @@
                 for (IntervalStats stats : mCurrentStats) {
                     stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
                     stats.updateBeginIdleTime(packageName, deviceUsageTime);
-                    mStatsChanged = true;
                 }
+
+                mAppIdleRollingWindow.update(packageName, currentTimeMillis,
+                        Event.SYSTEM_INTERACTION);
+                mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime);
+                mStatsChanged = true;
             }
         }
         // Persist the new OTA-related access stats.
         persistActiveStats();
     }
 
-    void onTimeChanged(long oldTime, long newTime, boolean resetBeginIdleTime) {
+    void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
+                       boolean resetBeginIdleTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
         loadActiveStats(newTime, resetBeginIdleTime);
+        refreshAppIdleRollingWindow(newTime, deviceUsageTime);
     }
 
     void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -181,7 +191,7 @@
 
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
-            rolloverStats(event.mTimeStamp);
+            rolloverStats(event.mTimeStamp, deviceUsageTime);
         }
 
         final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
@@ -211,6 +221,11 @@
             }
         }
 
+        if (event.mEventType != Event.CONFIGURATION_CHANGE) {
+            mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
+            mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
+        }
+
         notifyStatsChanged();
     }
 
@@ -222,6 +237,7 @@
         for (IntervalStats stats : mCurrentStats) {
             stats.updateBeginIdleTime(packageName, beginIdleTime);
         }
+        mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
         notifyStatsChanged();
     }
 
@@ -229,6 +245,7 @@
         for (IntervalStats stats : mCurrentStats) {
             stats.updateSystemLastUsedTime(packageName, lastUsedTime);
         }
+        mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
         notifyStatsChanged();
     }
 
@@ -387,9 +404,8 @@
     }
 
     long getBeginIdleTime(String packageName) {
-        final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
-        if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
             return -1;
         } else {
             return packageUsage.getBeginIdleTime();
@@ -397,9 +413,8 @@
     }
 
     long getSystemLastUsedTime(String packageName) {
-        final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
-        if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+        if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
             return -1;
         } else {
             return packageUsage.getLastTimeSystemUsed();
@@ -420,7 +435,7 @@
         }
     }
 
-    private void rolloverStats(final long currentTimeMillis) {
+    private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
         final long startTime = SystemClock.elapsedRealtime();
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
 
@@ -461,6 +476,8 @@
         }
         persistActiveStats();
 
+        refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
+
         final long totalTime = SystemClock.elapsedRealtime() - startTime;
         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
                 + " milliseconds");
@@ -503,6 +520,7 @@
                 }
             }
         }
+
         mStatsChanged = false;
         updateRolloverDeadline();
     }
@@ -516,6 +534,71 @@
                 mDailyExpiryDate.getTimeInMillis() + ")");
     }
 
+    private static void mergePackageStats(IntervalStats dst, IntervalStats src,
+                                          final long deviceUsageTime) {
+        dst.endTime = Math.max(dst.endTime, src.endTime);
+
+        final int srcPackageCount = src.packageStats.size();
+        for (int i = 0; i < srcPackageCount; i++) {
+            final String packageName = src.packageStats.keyAt(i);
+            final UsageStats srcStats = src.packageStats.valueAt(i);
+            UsageStats dstStats = dst.packageStats.get(packageName);
+            if (dstStats == null) {
+                dstStats = new UsageStats(srcStats);
+                dst.packageStats.put(packageName, dstStats);
+            } else {
+                dstStats.add(src.packageStats.valueAt(i));
+            }
+
+            // App idle times can not begin in the future. This happens if we had a time change.
+            if (dstStats.mBeginIdleTime > deviceUsageTime) {
+                dstStats.mBeginIdleTime = deviceUsageTime;
+            }
+        }
+    }
+
+    /**
+     * App idle operates on a rolling window of time. When we roll over time, we end up with a
+     * period of time where in-memory stats are empty and we don't hit the disk for older stats
+     * for performance reasons. Suddenly all apps will become idle.
+     *
+     * Instead, at times we do a deep query to find all the apps that have run in the past few
+     * days and keep the cached data up to date.
+     *
+     * @param currentTimeMillis
+     */
+    void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
+        // Start the rolling window for AppIdle requests.
+        final long startRangeMillis = currentTimeMillis -
+                mListener.getAppIdleRollingWindowDurationMillis();
+
+        List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
+                startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() {
+                    @Override
+                    public void combine(IntervalStats stats, boolean mutable,
+                                        List<IntervalStats> accumulatedResult) {
+                        IntervalStats accum;
+                        if (accumulatedResult.isEmpty()) {
+                            accum = new IntervalStats();
+                            accum.beginTime = stats.beginTime;
+                            accumulatedResult.add(accum);
+                        } else {
+                            accum = accumulatedResult.get(0);
+                        }
+
+                        mergePackageStats(accum, stats, deviceUsageTime);
+                    }
+                });
+
+        if (stats == null || stats.isEmpty()) {
+            mAppIdleRollingWindow = new IntervalStats();
+            mergePackageStats(mAppIdleRollingWindow,
+                    mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
+        } else {
+            mAppIdleRollingWindow = stats.get(0);
+        }
+    }
+
     //
     // -- DUMP related methods --
     //
@@ -538,6 +621,9 @@
             pw.println(" stats");
             printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
         }
+
+        pw.println("AppIdleRollingWindow cache");
+        printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
     }
 
     private String formatDateTime(long dateTime, boolean pretty) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c734fab..35a0464 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -858,7 +858,7 @@
                             .setOngoing(true)
                             .setTicker(title)
                             .setDefaults(0)  // please be quiet
-                            .setPriority(Notification.PRIORITY_LOW)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
                             .setColor(mContext.getColor(
                                     com.android.internal.R.color.system_notification_accent_color))
                             .setContentTitle(title)
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 5d5df95..aa95e1d 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -242,8 +242,13 @@
          */
         public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
 
+        /**
+         * Whether the call is associated with the work profile.
+         */
+        public static final int PROPERTY_WORK_CALL = 0x00000020;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000020
+        // Next PROPERTY value: 0x00000040
         //******************************************************************************************
 
         private final Uri mHandle;
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index 27ee804..a915d37 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -155,8 +155,7 @@
 
         // Get the list of apps registered for the DIAL intent with empty scheme
         Intent intent = new Intent(Intent.ACTION_DIAL);
-        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent,
-                PackageManager.MATCH_ENCRYPTION_DEFAULT);
+        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
 
         List<String> packageNames = new ArrayList<>();
 
@@ -209,7 +208,7 @@
 
         final List<String> result = new ArrayList<>();
         final List<ResolveInfo> resolveInfoList = context.getPackageManager()
-                .queryIntentActivities(intent, PackageManager.MATCH_ENCRYPTION_DEFAULT);
+                .queryIntentActivities(intent, 0);
         final int length = resolveInfoList.size();
         for (int i = 0; i < length; i++) {
             final ActivityInfo info = resolveInfoList.get(i).activityInfo;
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 5087080..426b240 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -49,7 +49,7 @@
  * <pre>
  * {@code
  * <service android:name="your.package.YourInCallServiceImplementation"
- *          android:permission="android.permission.BIND_IN_CALL_SERVICE">
+ *          android:permission="android.permission.BIND_INCALL_SERVICE">
  *      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
  *      <intent-filter>
  *          <action android:name="android.telecom.InCallService"/>
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
similarity index 80%
rename from core/java/android/service/notification/NotificationAdjustment.aidl
rename to telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
index 805fe2c..b7e78d1 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * Copyright 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.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.telecom;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+/**
+ * {@hide}
+ */
+parcelable ParcelableCallAnalytics;
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
new file mode 100644
index 0000000..e7c9672
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -0,0 +1,184 @@
+/*
+ * 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.telecom;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+@SystemApi
+public class ParcelableCallAnalytics implements Parcelable {
+    public static final int CALLTYPE_UNKNOWN = 0;
+    public static final int CALLTYPE_INCOMING = 1;
+    public static final int CALLTYPE_OUTGOING = 2;
+
+    // Constants for call technology
+    public static final int CDMA_PHONE = 0x1;
+    public static final int GSM_PHONE = 0x2;
+    public static final int IMS_PHONE = 0x4;
+    public static final int SIP_PHONE = 0x8;
+    public static final int THIRD_PARTY_PHONE = 0x10;
+
+    public static final long MILLIS_IN_5_MINUTES = 1000 * 60 * 5;
+    public static final long MILLIS_IN_1_SECOND = 1000;
+
+    public static final int STILL_CONNECTED = -1;
+
+    public static final Parcelable.Creator<ParcelableCallAnalytics> CREATOR =
+            new Parcelable.Creator<ParcelableCallAnalytics> () {
+
+                @Override
+                public ParcelableCallAnalytics createFromParcel(Parcel in) {
+                    return new ParcelableCallAnalytics(in);
+                }
+
+                @Override
+                public ParcelableCallAnalytics[] newArray(int size) {
+                    return new ParcelableCallAnalytics[size];
+                }
+            };
+
+    // The start time of the call in milliseconds since Jan. 1, 1970, rounded to the nearest
+    // 5 minute increment.
+    private final long startTimeMillis;
+
+    // The duration of the call, in milliseconds.
+    private final long callDurationMillis;
+
+    // ONE OF calltype_unknown, calltype_incoming, or calltype_outgoing
+    private final int callType;
+
+    // true if the call came in while another call was in progress or if the user dialed this call
+    // while in the middle of another call.
+    private final boolean isAdditionalCall;
+
+    // true if the call was interrupted by an incoming or outgoing call.
+    private final boolean isInterrupted;
+
+    // bitmask denoting which technologies a call used.
+    private final int callTechnologies;
+
+    // Any of the DisconnectCause codes, or STILL_CONNECTED.
+    private final int callTerminationCode;
+
+    // Whether the call is an emergency call
+    private final boolean isEmergencyCall;
+
+    // The package name of the connection service that this call used.
+    private final String connectionService;
+
+    // Whether the call object was created from an existing connection.
+    private final boolean isCreatedFromExistingConnection;
+
+    public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
+            boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
+            int callTerminationCode, boolean isEmergencyCall, String connectionService,
+            boolean isCreatedFromExistingConnection) {
+        this.startTimeMillis = startTimeMillis;
+        this.callDurationMillis = callDurationMillis;
+        this.callType = callType;
+        this.isAdditionalCall = isAdditionalCall;
+        this.isInterrupted = isInterrupted;
+        this.callTechnologies = callTechnologies;
+        this.callTerminationCode = callTerminationCode;
+        this.isEmergencyCall = isEmergencyCall;
+        this.connectionService = connectionService;
+        this.isCreatedFromExistingConnection = isCreatedFromExistingConnection;
+    }
+
+    public ParcelableCallAnalytics(Parcel in) {
+        startTimeMillis = in.readLong();
+        callDurationMillis = in.readLong();
+        callType = in.readInt();
+        isAdditionalCall = readByteAsBoolean(in);
+        isInterrupted = readByteAsBoolean(in);
+        callTechnologies = in.readInt();
+        callTerminationCode = in.readInt();
+        isEmergencyCall = readByteAsBoolean(in);
+        connectionService = in.readString();
+        isCreatedFromExistingConnection = readByteAsBoolean(in);
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(startTimeMillis);
+        out.writeLong(callDurationMillis);
+        out.writeInt(callType);
+        writeBooleanAsByte(out, isAdditionalCall);
+        writeBooleanAsByte(out, isInterrupted);
+        out.writeInt(callTechnologies);
+        out.writeInt(callTerminationCode);
+        writeBooleanAsByte(out, isEmergencyCall);
+        out.writeString(connectionService);
+        writeBooleanAsByte(out, isCreatedFromExistingConnection);
+    }
+
+    public long getStartTimeMillis() {
+        return startTimeMillis;
+    }
+
+    public long getCallDurationMillis() {
+        return callDurationMillis;
+    }
+
+    public int getCallType() {
+        return callType;
+    }
+
+    public boolean isAdditionalCall() {
+        return isAdditionalCall;
+    }
+
+    public boolean isInterrupted() {
+        return isInterrupted;
+    }
+
+    public int getCallTechnologies() {
+        return callTechnologies;
+    }
+
+    public int getCallTerminationCode() {
+        return callTerminationCode;
+    }
+
+    public boolean isEmergencyCall() {
+        return isEmergencyCall;
+    }
+
+    public String getConnectionService() {
+        return connectionService;
+    }
+
+    public boolean isCreatedFromExistingConnection() {
+        return isCreatedFromExistingConnection;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private static void writeBooleanAsByte(Parcel out, boolean b) {
+        out.writeByte((byte) (b ? 1 : 0));
+    }
+
+    private static boolean readByteAsBoolean(Parcel in) {
+        return (in.readByte() == 1);
+    }
+}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index b18af33..b56ce73 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -687,7 +687,7 @@
                 .append("] PhoneAccount: ")
                 .append(mAccountHandle)
                 .append(" Capabilities: ")
-                .append(mCapabilities)
+                .append(capabilitiesToString(mCapabilities))
                 .append(" Schemes: ");
         for (String scheme : mSupportedUriSchemes) {
             sb.append(scheme)
@@ -698,4 +698,42 @@
         sb.append("]");
         return sb.toString();
     }
+
+    /**
+     * Generates a string representation of a capabilities bitmask.
+     *
+     * @param capabilities The capabilities bitmask.
+     * @return String representation of the capabilities bitmask.
+     */
+    private String capabilitiesToString(int capabilities) {
+        StringBuilder sb = new StringBuilder();
+        if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
+            sb.append("Video ");
+        }
+        if (hasCapabilities(CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
+            sb.append("Presence ");
+        }
+        if (hasCapabilities(CAPABILITY_CALL_PROVIDER)) {
+            sb.append("CallProvider ");
+        }
+        if (hasCapabilities(CAPABILITY_CALL_SUBJECT)) {
+            sb.append("CallSubject ");
+        }
+        if (hasCapabilities(CAPABILITY_CONNECTION_MANAGER)) {
+            sb.append("ConnectionMgr ");
+        }
+        if (hasCapabilities(CAPABILITY_EMERGENCY_CALLS_ONLY)) {
+            sb.append("EmergOnly ");
+        }
+        if (hasCapabilities(CAPABILITY_MULTI_USER)) {
+            sb.append("MultiUser ");
+        }
+        if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) {
+            sb.append("PlaceEmerg ");
+        }
+        if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
+            sb.append("SimSub ");
+        }
+        return sb.toString();
+    }
 }
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9eb1665..d45b26f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -14,6 +14,7 @@
 
 package android.telecom;
 
+import android.Manifest;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.ComponentName;
@@ -1377,6 +1378,27 @@
         }
     }
 
+    /**
+     * Dumps telecom analytics for uploading.
+     *
+     * @return
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.DUMP)
+    public List<ParcelableCallAnalytics> dumpAnalytics() {
+        ITelecomService service = getTelecomService();
+        List<ParcelableCallAnalytics> result = null;
+        if (service != null) {
+            try {
+                result = service.dumpCallAnalytics();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error dumping call analytics", e);
+            }
+        }
+        return result;
+    }
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index dabf706..216603c 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -16,13 +16,23 @@
 
 package android.telecom;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents attributes of video calls.
  */
 public class VideoProfile implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({QUALITY_UNKNOWN, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_DEFAULT})
+    public @interface VideoQuality {}
+
     /**
      * "Unknown" video quality.
      * @hide
@@ -48,6 +58,14 @@
      */
     public static final int QUALITY_DEFAULT = 4;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            flag = true,
+            value = {STATE_AUDIO_ONLY, STATE_TX_ENABLED, STATE_RX_ENABLED, STATE_BIDIRECTIONAL,
+                    STATE_PAUSED})
+    public @interface VideoState {}
+
     /**
      * Used when answering or dialing a call to indicate that the call does not have a video
      * component.
@@ -107,7 +125,7 @@
      *
      * @param videoState The video state.
      */
-    public VideoProfile(int videoState) {
+    public VideoProfile(@VideoState int videoState) {
         this(videoState, QUALITY_DEFAULT);
     }
 
@@ -117,7 +135,7 @@
      * @param videoState The video state.
      * @param quality The video quality.
      */
-    public VideoProfile(int videoState, int quality) {
+    public VideoProfile(@VideoState int videoState, @VideoQuality int quality) {
         mVideoState = videoState;
         mQuality = quality;
     }
@@ -130,6 +148,7 @@
      * {@link VideoProfile#STATE_RX_ENABLED},
      * {@link VideoProfile#STATE_PAUSED}.
      */
+    @VideoState
     public int getVideoState() {
         return mVideoState;
     }
@@ -139,6 +158,7 @@
      * Valid values: {@link VideoProfile#QUALITY_HIGH}, {@link VideoProfile#QUALITY_MEDIUM},
      * {@link VideoProfile#QUALITY_LOW}, {@link VideoProfile#QUALITY_DEFAULT}.
      */
+    @VideoQuality
     public int getQuality() {
         return mQuality;
     }
@@ -211,7 +231,7 @@
      * @param videoState The video state.
      * @return String representation of the video state.
      */
-    public static String videoStateToString(int videoState) {
+    public static String videoStateToString(@VideoState int videoState) {
         StringBuilder sb = new StringBuilder();
         sb.append("Audio");
 
@@ -240,7 +260,7 @@
      * @param videoState The video state.
      * @return {@code True} if the video state is audio only, {@code false} otherwise.
      */
-    public static boolean isAudioOnly(int videoState) {
+    public static boolean isAudioOnly(@VideoState int videoState) {
         return !hasState(videoState, VideoProfile.STATE_TX_ENABLED)
                 && !hasState(videoState, VideoProfile.STATE_RX_ENABLED);
     }
@@ -251,7 +271,7 @@
      * @param videoState The video state.
      * @return {@code True} if video transmission or reception is enabled, {@code false} otherwise.
      */
-    public static boolean isVideo(int videoState) {
+    public static boolean isVideo(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_TX_ENABLED)
                 || hasState(videoState, VideoProfile.STATE_RX_ENABLED)
                 || hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
@@ -263,7 +283,7 @@
      * @param videoState The video state.
      * @return {@code True} if video transmission is enabled, {@code false} otherwise.
      */
-    public static boolean isTransmissionEnabled(int videoState) {
+    public static boolean isTransmissionEnabled(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_TX_ENABLED);
     }
 
@@ -273,7 +293,7 @@
      * @param videoState The video state.
      * @return {@code True} if video reception is enabled, {@code false} otherwise.
      */
-    public static boolean isReceptionEnabled(int videoState) {
+    public static boolean isReceptionEnabled(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_RX_ENABLED);
     }
 
@@ -283,7 +303,7 @@
      * @param videoState The video state.
      * @return {@code True} if the video is bi-directional, {@code false} otherwise.
      */
-    public static boolean isBidirectional(int videoState) {
+    public static boolean isBidirectional(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
     }
 
@@ -293,7 +313,7 @@
      * @param videoState The video state.
      * @return {@code True} if the video is paused, {@code false} otherwise.
      */
-    public static boolean isPaused(int videoState) {
+    public static boolean isPaused(@VideoState int videoState) {
         return hasState(videoState, VideoProfile.STATE_PAUSED);
     }
 
@@ -304,7 +324,7 @@
      * @param state The state to check.
      * @return {@code True} if the state is set.
      */
-    private static boolean hasState(int videoState, int state) {
+    private static boolean hasState(@VideoState int videoState, @VideoState int state) {
         return (videoState & state) == state;
     }
 
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 856e210..af36b3e 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telecom;
 
 import android.content.ComponentName;
+import android.telecom.ParcelableCallAnalytics;
 import android.telecom.PhoneAccountHandle;
 import android.net.Uri;
 import android.os.Bundle;
@@ -143,6 +144,11 @@
      */
     String getSystemDialerPackage();
 
+    /**
+    * @see TelecomServiceImpl#dumpCallAnalytics
+    */
+    List<ParcelableCallAnalytics> dumpCallAnalytics();
+
     //
     // Internal system apis relating to call management.
     //
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6ffc026..10815b3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -473,6 +473,15 @@
     public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
 
     /**
+     * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference.
+     * Some operators support Wi-Fi Calling only, not VoLTE.
+     * They don't need "Cellular preferred" option.
+     * In this case, set uneditalbe attribute for preferred preference.
+     * @hide
+     */
+    public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+
+    /**
      * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
      * prevent manual operator selection. If false, this SIM setting will be ignored and manual
      * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -486,6 +495,13 @@
      */
     public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
 
+    /**
+     * Boolean indicating if intent for emergency call state changes should be broadcast
+     * @hide
+     */
+    public static final String KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL =
+            "broadcast_emergency_call_state_changes_bool";
+
     // These variables are used by the MMS service and exposed through another API, {@link
     // SmsManager}. The variable names and string values are copied from there.
     public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -553,6 +569,23 @@
     public static final String BOOL_ALLOW_VIDEO_PAUSE =
             "bool_allow_video_pause";
 
+
+    /**
+     * Flag indicating whether the carrier supports RCS presence indication for video calls.  When
+     * {@code true}, the carrier supports RCS presence indication for video calls.  When presence
+     * is supported, the device should use the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
+     * whether each contact supports video calling.  The UI is made aware that presence is enabled
+     * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
+     * and can choose to hide or show the video calling icon based on whether a contact supports
+     * video.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -612,6 +645,7 @@
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
         sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
+        sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
 
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
@@ -628,6 +662,7 @@
         sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
         sDefaults.putBoolean(BOOL_ALLOW_EMERGENCY_VIDEO_CALLS, false);
         sDefaults.putBoolean(BOOL_ALLOW_VIDEO_PAUSE, true);
+        sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -662,6 +697,7 @@
         sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
         sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
         sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
+        sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 8537f9c..c680999 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -153,6 +153,17 @@
      * @hide
      */
     public static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18;
+
+    /** @hide */
+    public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
+            (1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_IS95B - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_1xRTT - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_0 - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_A - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
+
     /**
      * Available registration states for GSM, UMTS and CDMA.
      */
@@ -1141,16 +1152,8 @@
     }
 
     /** @hide */
-    public static boolean hasCdma(int radioTechnologyBitmask) {
-        int cdmaBitmask = (RIL_RADIO_TECHNOLOGY_IS95A
-                | RIL_RADIO_TECHNOLOGY_IS95B
-                | RIL_RADIO_TECHNOLOGY_1xRTT
-                | RIL_RADIO_TECHNOLOGY_EVDO_0
-                | RIL_RADIO_TECHNOLOGY_EVDO_A
-                | RIL_RADIO_TECHNOLOGY_EVDO_B
-                | RIL_RADIO_TECHNOLOGY_EHRPD);
-
-        return ((radioTechnologyBitmask & cdmaBitmask) != 0);
+    public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) {
+        return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0;
     }
 
     /** @hide */
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 9998937..39f3213 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -24,6 +24,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Looper;
 import android.telephony.Rlog;
 import android.os.Handler;
 import android.os.Message;
@@ -347,7 +348,31 @@
      * for #onSubscriptionsChanged to be invoked.
      */
     public static class OnSubscriptionsChangedListener {
-        private final Handler mHandler  = new Handler() {
+        private final Handler mHandler;
+
+        public OnSubscriptionsChangedListener() {
+            mHandler = new OnSubscriptionsChangedListenerHandler();
+        }
+
+        /**
+         * Contructor that takes in looper as parameter in case a subclass/instantiation needs
+         * to use a specific looper (like in tests where mainLooper may need to be used).
+         * @param looper Looper to be used for mHandler
+         * @hide
+         */
+        protected OnSubscriptionsChangedListener(Looper looper) {
+            mHandler = new OnSubscriptionsChangedListenerHandler(looper);
+        }
+
+        private class OnSubscriptionsChangedListenerHandler extends Handler {
+            private OnSubscriptionsChangedListenerHandler() {
+                super();
+            }
+
+            private OnSubscriptionsChangedListenerHandler(Looper looper) {
+                super(looper);
+            }
+
             @Override
             public void handleMessage(Message msg) {
                 if (DBG) {
@@ -355,7 +380,7 @@
                 }
                 OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
             }
-        };
+        }
 
         /**
          * Callback invoked when there is any change to any SubscriptionInfo. Typically
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0b5aaa3..43fe9d1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -24,6 +24,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Uri;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.os.Bundle;
@@ -31,6 +32,7 @@
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
 import android.util.Log;
 
 import com.android.internal.telecom.ITelecomService;
@@ -3996,19 +3998,6 @@
 
     /** @hide */
     @SystemApi
-    public boolean isSimPinEnabled() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isSimPinEnabled(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isSimPinEnabled", e);
-        }
-        return false;
-    }
-
-    /** @hide */
-    @SystemApi
     public boolean supplyPin(String pin) {
         try {
             ITelephony telephony = getITelephony();
@@ -4865,4 +4854,43 @@
         }
         return null;
     }
+
+    /**
+     * Returns the URI for the per-account voicemail ringtone set in Phone settings.
+     *
+     * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+     * voicemail ringtone.
+     * @return The URI for the ringtone to play when receiving a voicemail from a specific
+     * PhoneAccount.
+     */
+    public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getVoicemailRingtoneUri(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getVoicemailRingtoneUri", e);
+        }
+        return null;
+    }
+
+    /**
+     * Returns whether vibration is set for voicemail notification in Phone settings.
+     *
+     * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+     * voicemail vibration setting.
+     * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+     */
+    public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isVoicemailVibrationEnabled(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e);
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 59a8a67..b9d7297 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -53,6 +53,9 @@
     private static final String TAG = "CallerInfo";
     private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
 
+    public static final long USER_TYPE_CURRENT = 0;
+    public static final long USER_TYPE_WORK = 1;
+
     /**
      * Please note that, any one of these member variables can be null,
      * and any accesses to them should be prepared to handle such a case.
@@ -103,6 +106,8 @@
     public Uri contactRefUri;
     public String lookupKey;
 
+    public long userType;
+
     /**
      * Contact display photo URI.  If a contact has no display photo but a thumbnail, it'll be
      * the thumbnail URI instead.
@@ -154,6 +159,7 @@
         // TODO: Move all the basic initialization here?
         mIsEmergency = false;
         mIsVoiceMail = false;
+        userType = USER_TYPE_CURRENT;
     }
 
     /**
@@ -173,6 +179,7 @@
         info.cachedPhoto = null;
         info.isCachedPhotoCurrent = false;
         info.contactExists = false;
+        info.userType = USER_TYPE_CURRENT;
 
         if (VDBG) Rlog.v(TAG, "getCallerInfo() based on cursor...");
 
@@ -225,6 +232,9 @@
                             Rlog.v(TAG, "==> got info.contactIdOrZero: " + info.contactIdOrZero);
                         }
                     }
+                    if (Contacts.isEnterpriseContactId(contactId)) {
+                        info.userType = USER_TYPE_WORK;
+                    }
                 } else {
                     // No valid columnIndex, so we can't look up person_id.
                     Rlog.w(TAG, "Couldn't find contact_id column for " + contactRef);
@@ -628,6 +638,7 @@
     /**
      * @return a string debug representation of this instance.
      */
+    @Override
     public String toString() {
         // Warning: never check in this file with VERBOSE_DEBUG = true
         // because that will result in PII in the system log.
@@ -658,6 +669,7 @@
                     .append("\nemergency: " + mIsEmergency)
                     .append("\nvoicemail " + mIsVoiceMail)
                     .append("\ncontactExists " + contactExists)
+                    .append("\nuserType " + userType)
                     .append(" }")
                     .toString();
         } else {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 8172e94..62f294c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -18,7 +18,9 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.net.Uri;
 import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
 import android.telephony.CellInfo;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.ModemActivityInfo;
@@ -180,13 +182,6 @@
     boolean isRadioOnForSubscriber(int subId, String callingPackage);
 
     /**
-     * Check if the SIM pin lock is enabled.
-     * @return true if the SIM pin lock is enabled.
-     * @param callingPackage The package making the call.
-     */
-    boolean isSimPinEnabled(String callingPackage);
-
-    /**
      * Supply a pin to unlock the SIM.  Blocks until a result is determined.
      * @param pin The pin to check.
      * @return whether the operation was a success.
@@ -966,7 +961,7 @@
      * Returns the Status of Wi-Fi Calling
      */
     boolean isWifiCallingAvailable();
-    
+
     /**
      * Returns the Status of Volte
      */
@@ -1014,4 +1009,23 @@
      * @return Service state on specified subscription.
      */
     ServiceState getServiceStateForSubscriber(int subId, String callingPackage);
+
+    /**
+     * Returns the URI for the per-account voicemail ringtone set in Phone settings.
+     *
+     * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+     * voicemail ringtone.
+     * @return The URI for the ringtone to play when receiving a voicemail from a specific
+     * PhoneAccount.
+     */
+    Uri getVoicemailRingtoneUri(in PhoneAccountHandle accountHandle);
+
+    /**
+     * Returns whether vibration is set for voicemail notification in Phone settings.
+     *
+     * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+     * voicemail vibration setting.
+     * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+     */
+    boolean isVoicemailVibrationEnabled(in PhoneAccountHandle accountHandle);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
new file mode 100644
index 0000000..5dffa28
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.Bundle;
+
+
+/**
+ * Interface used to interact with the Telephony debug service.
+ *
+ * {@hide}
+ */
+interface ITelephonyDebug {
+
+    /**
+     * Write telephony event
+     * @param timestamp returned by System.currentTimeMillis()
+     * @param phoneId for which event is written
+     * @param tag constant defined in TelephonyEventLog
+     * @param param1 optional
+     * @param param2 optional
+     * @param data optional
+     */
+    void writeEvent(long timestamp, int phoneId, int tag, int param1, int param2, in Bundle data);
+}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index a183de5..ecd89ed 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -35,17 +35,17 @@
         IDLE, RINGING, OFFHOOK;
     };
 
-   /**
-     * The state of a data connection.
-     * <ul>
-     * <li>CONNECTED = IP traffic should be available</li>
-     * <li>CONNECTING = Currently setting up data connection</li>
-     * <li>DISCONNECTED = IP not available</li>
-     * <li>SUSPENDED = connection is created but IP traffic is
-     *                 temperately not available. i.e. voice call is in place
-     *                 in 2G network</li>
-     * </ul>
-     */
+    /**
+      * The state of a data connection.
+      * <ul>
+      * <li>CONNECTED = IP traffic should be available</li>
+      * <li>CONNECTING = Currently setting up data connection</li>
+      * <li>DISCONNECTED = IP not available</li>
+      * <li>SUSPENDED = connection is created but IP traffic is
+      *                 temperately not available. i.e. voice call is in place
+      *                 in 2G network</li>
+      * </ul>
+      */
     public enum DataState {
         CONNECTED, CONNECTING, DISCONNECTED, SUSPENDED;
     };
@@ -89,6 +89,7 @@
     public static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
     public static final String DATA_NETWORK_ROAMING_KEY = "networkRoaming";
     public static final String PHONE_IN_ECM_STATE = "phoneinECMState";
+    public static final String PHONE_IN_EMERGENCY_CALL = "phoneInEmergencyCall";
 
     public static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged";
 
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index d2e4de3..c70f8cf 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -75,6 +75,7 @@
      */
     public static final String ACTION_RADIO_TECHNOLOGY_CHANGED
             = "android.intent.action.RADIO_TECHNOLOGY";
+
     /**
      * <p>Broadcast Action: The emergency callback mode is changed.
      * <ul>
@@ -94,6 +95,28 @@
      */
     public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
             = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
+
+    /**
+     * <p>Broadcast Action: The emergency call state is changed.
+     * <ul>
+     *   <li><em>phoneInEmergencyCall</em> - A boolean value, true if phone in emergency call,
+     *   false otherwise</li>
+     * </ul>
+     * <p class="note">
+     * You can <em>not</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+     * android.content.IntentFilter) Context.registerReceiver()}.
+     *
+     * <p class="note">
+     * Requires no permission.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     */
+    public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED
+            = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
+
     /**
      * Broadcast Action: The phone's signal strength has changed. The intent will have the
      * following extra values:</p>
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 2813df5..81aa6c6 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -34,7 +34,6 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
-import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageItemInfo;
@@ -97,14 +96,30 @@
     }
 
     @Override
-
     public int[] getPackageGids(String packageName) throws NameNotFoundException {
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public int[] getPackageGids(String packageName, int flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @Override
-    public int getPackageUid(String packageName, int userHandle)
+    public int getPackageUidAsUser(String packageName, int flags, int userHandle)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public int getPackageUidAsUser(String packageName, int userHandle)
             throws NameNotFoundException {
         throw new UnsupportedOperationException();
     }
@@ -134,7 +149,14 @@
 
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
-    throws NameNotFoundException {
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
         throw new UnsupportedOperationException();
     }
 
@@ -175,7 +197,7 @@
 
     /** @hide */
     @Override
-    public List<PackageInfo> getInstalledPackages(int flags, int userId) {
+    public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
         throw new UnsupportedOperationException();
     }
 
@@ -300,21 +322,25 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
     @Override
     public byte[] getEphemeralCookie() {
         return new byte[0];
     }
 
+    /** @hide */
     @Override
     public boolean isEphemeralApplication() {
         return false;
     }
 
+    /** @hide */
     @Override
     public int getEphemeralCookieMaxSizeBytes() {
         return 0;
     }
 
+    /** @hide */
     @Override
     public boolean setEphemeralCookie(@NonNull byte[] cookie) {
         return false;
@@ -356,7 +382,7 @@
 
     /** @hide */
     @Override
-    public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags, int userId) {
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
         throw new UnsupportedOperationException();
     }
 
@@ -747,7 +773,7 @@
      * @hide - to match hiding in superclass
      */
     @Override
-    public void getPackageSizeInfo(String packageName, int userHandle,
+    public void getPackageSizeInfoAsUser(String packageName, int userHandle,
             IPackageStatsObserver observer) {
         throw new UnsupportedOperationException();
     }
@@ -820,7 +846,7 @@
     @Override
     public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
             int flags, String installerPackageName, Uri verificationURI,
-            ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
+            ContainerEncryptionParams encryptionParams) {
         throw new UnsupportedOperationException();
     }
 
@@ -891,7 +917,7 @@
      * @hide
      */
     @Override
-    public int getIntentVerificationStatus(String packageName, int userId) {
+    public int getIntentVerificationStatusAsUser(String packageName, int userId) {
         throw new UnsupportedOperationException();
     }
 
@@ -899,7 +925,7 @@
      * @hide
      */
     @Override
-    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+    public boolean updateIntentVerificationStatusAsUser(String packageName, int status, int userId) {
         throw new UnsupportedOperationException();
     }
 
@@ -916,16 +942,30 @@
         throw new UnsupportedOperationException();
     }
 
-    @Override
+    /** {@removed} */
+    @Deprecated
     public String getDefaultBrowserPackageName(int userId) {
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
     @Override
+    public String getDefaultBrowserPackageNameAsUser(int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@removed} */
+    @Deprecated
     public boolean setDefaultBrowserPackageName(String packageName, int userId) {
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    @Override
+    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide
      */
@@ -957,7 +997,7 @@
     @Override
     public void installPackageWithVerification(Uri packageURI,
             PackageInstallObserver observer, int flags, String installerPackageName,
-            Uri verificationURI, ManifestDigest manifestDigest,
+            Uri verificationURI,
             ContainerEncryptionParams encryptionParams) {
         throw new UnsupportedOperationException();
     }
diff --git a/tests/FeatureSplit/base/AndroidManifest.xml b/tests/FeatureSplit/base/AndroidManifest.xml
index 989e802..e82b3b9 100644
--- a/tests/FeatureSplit/base/AndroidManifest.xml
+++ b/tests/FeatureSplit/base/AndroidManifest.xml
@@ -16,7 +16,15 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.test.split.feature">
-    <application android:label="@string/app_title"
-        android:hasCode="false">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+    <application android:label="@string/app_title">
+        <activity android:name=".ActivityMain" android:label="Feature Base">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/FeatureSplit/base/res/layout/main.xml b/tests/FeatureSplit/base/res/layout/main.xml
new file mode 100644
index 0000000..f01b920
--- /dev/null
+++ b/tests/FeatureSplit/base/res/layout/main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="16dp">
+    <TextView android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:textAppearance="?android:textAppearanceLarge" />
+</RelativeLayout>
diff --git a/tests/FeatureSplit/base/res/values/values.xml b/tests/FeatureSplit/base/res/values/values.xml
index 564d301..854a8bb 100644
--- a/tests/FeatureSplit/base/res/values/values.xml
+++ b/tests/FeatureSplit/base/res/values/values.xml
@@ -16,6 +16,7 @@
 
 <resources>
     <string name="app_title">FeatureSplit APK</string>
+    <string name="base">Base</string>
 
     <item type="id" name="test_id"/>
     <integer name="test_integer">100</integer>
diff --git a/tests/FeatureSplit/base/src/com/android/test/split/feature/ActivityMain.java b/tests/FeatureSplit/base/src/com/android/test/split/feature/ActivityMain.java
new file mode 100644
index 0000000..6cca7c3
--- /dev/null
+++ b/tests/FeatureSplit/base/src/com/android/test/split/feature/ActivityMain.java
@@ -0,0 +1,31 @@
+/*
+ * 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.test.split.feature;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ActivityMain extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        ((TextView) findViewById(R.id.text)).setText(R.string.base);
+    }
+}
+
diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk
index adfb575..aa222dd 100644
--- a/tests/FeatureSplit/feature1/Android.mk
+++ b/tests/FeatureSplit/feature1/Android.mk
@@ -22,10 +22,12 @@
 LOCAL_MODULE_TAGS := tests
 
 featureOf := FeatureSplitBase
+
+LOCAL_APK_LIBRARIES := $(featureOf)
 featureOfApk := $(call intermediates-dir-for,APPS,$(featureOf))/package.apk
 localRStamp := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)/src/R.stamp
 $(localRStamp): $(featureOfApk)
 
-LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk)
+LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk) --custom-package com.android.test.split.feature.one
 
 include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml
index 2aadc6d..42619b6 100644
--- a/tests/FeatureSplit/feature1/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature1/AndroidManifest.xml
@@ -17,5 +17,15 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.test.split.feature"
     featureName="feature1">
-    <application android:hasCode="false" />
+
+    <uses-sdk android:minSdkVersion="21" />
+
+    <application>
+        <activity android:name=".one.One" android:label="Feature One">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
 </manifest>
diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml
index 70eb56a..10dbd97 100644
--- a/tests/FeatureSplit/feature1/res/values/values.xml
+++ b/tests/FeatureSplit/feature1/res/values/values.xml
@@ -15,6 +15,7 @@
 -->
 
 <resources>
+    <string name="feature_string">Feature1</string>
     <item type="id" name="test_id2"/>
     <integer name="test_integer2">200</integer>
     <color name="test_color2">#00ff00</color>
diff --git a/tests/FeatureSplit/feature1/src/com/android/test/split/feature/one/One.java b/tests/FeatureSplit/feature1/src/com/android/test/split/feature/one/One.java
new file mode 100644
index 0000000..def1339
--- /dev/null
+++ b/tests/FeatureSplit/feature1/src/com/android/test/split/feature/one/One.java
@@ -0,0 +1,31 @@
+/*
+ * 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.test.split.feature.one;
+
+import com.android.test.split.feature.ActivityMain;
+
+import android.widget.TextView;
+import android.os.Bundle;
+
+public class One extends ActivityMain {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ((TextView) findViewById(com.android.test.split.feature.R.id.text))
+                .setText(R.string.feature_string);
+    }
+}
diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk
index e69cbe8..1a0322b 100644
--- a/tests/FeatureSplit/feature2/Android.mk
+++ b/tests/FeatureSplit/feature2/Android.mk
@@ -24,6 +24,8 @@
 featureOf := FeatureSplitBase
 featureAfter := FeatureSplit1
 
+LOCAL_APK_LIBRARIES := $(featureOf)
+
 featureOfApk := $(call intermediates-dir-for,APPS,$(featureOf))/package.apk
 featureAfterApk := $(call intermediates-dir-for,APPS,$(featureAfter))/package.apk
 localRStamp := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)/src/R.stamp
@@ -31,5 +33,6 @@
 
 LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk)
 LOCAL_AAPT_FLAGS += --feature-after $(featureAfterApk)
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two
 
 include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature2/AndroidManifest.xml b/tests/FeatureSplit/feature2/AndroidManifest.xml
index d139900..b50044a 100644
--- a/tests/FeatureSplit/feature2/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature2/AndroidManifest.xml
@@ -17,5 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.test.split.feature"
     featureName="feature2">
+
+    <uses-sdk android:minSdkVersion="21" />
+
     <application android:hasCode="false"/>
 </manifest>
diff --git a/tests/RenderScriptTests/Android.mk b/tests/RenderScriptTests/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/tests/RenderScriptTests/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/tests/RenderScriptTests/FBOTest/Android.mk b/tests/RenderScriptTests/FBOTest/Android.mk
deleted file mode 100644
index 7a578d9..0000000
--- a/tests/RenderScriptTests/FBOTest/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := FBOTest
-
-LOCAL_SDK_VERSION := 17
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/FBOTest/AndroidManifest.xml b/tests/RenderScriptTests/FBOTest/AndroidManifest.xml
deleted file mode 100644
index 788e856..0000000
--- a/tests/RenderScriptTests/FBOTest/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.fbotest">
-    <application android:label="_FBOTest">
-        <activity android:name="FBOTest"
-                  android:label="FBO Base Test"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity android:name="FBOSync"
-                  android:label="FBO Sync Test"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
deleted file mode 100644
index f7353fd..0000000
--- a/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/FBOTest/res/raw/robot.a3d b/tests/RenderScriptTests/FBOTest/res/raw/robot.a3d
deleted file mode 100644
index f48895c..0000000
--- a/tests/RenderScriptTests/FBOTest/res/raw/robot.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSync.java b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSync.java
deleted file mode 100644
index d30ad7e..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSync.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fbotest;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.MenuInflater;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-import android.net.Uri;
-
-import java.lang.Runtime;
-
-public class FBOSync extends Activity {
-
-    private FBOSyncView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new FBOSyncView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.pause();
-    }
-}
-
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSyncRS.java b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSyncRS.java
deleted file mode 100644
index 57a117c..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSyncRS.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fbotest;
-
-import java.io.Writer;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.Element.DataType;
-import android.renderscript.Element.DataKind;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.renderscript.Type.Builder;
-import android.util.Log;
-
-
-public class FBOSyncRS {
-
-    public FBOSyncRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    public void surfaceChanged() {
-        mRS.getWidth();
-        mRS.getHeight();
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Sampler mSampler;
-    private ProgramStore mPSBackground;
-    private ProgramFragment mPFBackground;
-    private ProgramVertex mPVBackground;
-    private ProgramVertexFixedFunction.Constants mPVA;
-
-    private Allocation mGridImage;
-    private Allocation mOffscreen;
-    private Allocation mOffscreenDepth;
-    private Allocation mAllocPV;
-    private Allocation mReadBackTest;
-
-    private Font mItalic;
-    private Allocation mTextAlloc;
-
-    private ScriptField_MeshInfo mMeshes;
-    private ScriptC_fbosync mScript;
-
-
-    public void onActionDown(float x, float y) {
-        mScript.invoke_onActionDown(x, y);
-    }
-
-    public void onActionScale(float scale) {
-        mScript.invoke_onActionScale(scale);
-    }
-
-    public void onActionMove(float x, float y) {
-        mScript.invoke_onActionMove(x, y);
-    }
-
-    private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
-
-        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
-        b.setDitherEnabled(false);
-        b.setDepthMaskEnabled(true);
-        mPSBackground = b.create();
-
-        mScript.set_gPFSBackground(mPSBackground);
-    }
-
-    private void initPF() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.CLAMP);
-        mSampler = bs.create();
-
-        ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
-        b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                     ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mPFBackground = b.create();
-        mPFBackground.bindSampler(mSampler, 0);
-
-        mScript.set_gPFBackground(mPFBackground);
-    }
-
-    private void initPV() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mPVBackground = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVA);
-
-        mScript.set_gPVBackground(mPVBackground);
-    }
-
-    private void loadImage() {
-        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
-                                                         Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                         Allocation.USAGE_GRAPHICS_TEXTURE);
-        mScript.set_gTGrid(mGridImage);
-    }
-
-    private void initTextAllocation(String fileName) {
-        String allocString = "Displaying file: " + fileName;
-        mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT);
-        mScript.set_gTextAlloc(mTextAlloc);
-    }
-
-    private void initMeshes(FileA3D model) {
-        int numEntries = model.getIndexEntryCount();
-        int numMeshes = 0;
-        for (int i = 0; i < numEntries; i ++) {
-            FileA3D.IndexEntry entry = model.getIndexEntry(i);
-            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                numMeshes ++;
-            }
-        }
-
-        if (numMeshes > 0) {
-            mMeshes = new ScriptField_MeshInfo(mRS, numMeshes);
-
-            for (int i = 0; i < numEntries; i ++) {
-                FileA3D.IndexEntry entry = model.getIndexEntry(i);
-                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                    Mesh mesh = entry.getMesh();
-                    mMeshes.set_mMesh(i, mesh, false);
-                    mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false);
-                }
-            }
-            mMeshes.copyAll();
-        } else {
-            throw new RSRuntimeException("No valid meshes in file");
-        }
-
-        mScript.bind_gMeshes(mMeshes);
-        mScript.invoke_updateMeshInfo();
-    }
-
-    public void loadA3DFile(String path) {
-        FileA3D model = FileA3D.createFromFile(mRS, path);
-        initMeshes(model);
-        initTextAllocation(path);
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_fbosync(mRS, mRes, R.raw.fbosync);
-
-        initPFS();
-        initPF();
-        initPV();
-
-        loadImage();
-
-        Type.Builder b = new Type.Builder(mRS, Element.RGBA_8888(mRS));
-        b.setX(512).setY(512);
-        mOffscreen = Allocation.createTyped(mRS,
-                                            b.create(),
-                                            Allocation.USAGE_SCRIPT |
-                                            Allocation.USAGE_GRAPHICS_TEXTURE |
-                                            Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gOffscreen(mOffscreen);
-
-        mReadBackTest = Allocation.createTyped(mRS,
-                                               b.create(),
-                                               Allocation.USAGE_SCRIPT |
-                                               Allocation.USAGE_GRAPHICS_TEXTURE);
-        mScript.set_gReadBackTest(mReadBackTest);
-
-        b = new Type.Builder(mRS,
-                             Element.createPixel(mRS, DataType.UNSIGNED_16,
-                             DataKind.PIXEL_DEPTH));
-        b.setX(512).setY(512);
-        mOffscreenDepth = Allocation.createTyped(mRS,
-                                                 b.create(),
-                                                 Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gOffscreenDepth(mOffscreenDepth);
-
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
-        initMeshes(model);
-
-        mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
-        mScript.set_gItalic(mItalic);
-
-        initTextAllocation("R.raw.robot");
-
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSyncView.java b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSyncView.java
deleted file mode 100644
index 6a85628..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOSyncView.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fbotest;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.ScaleGestureDetector;
-import android.util.Log;
-
-public class FBOSyncView extends RSSurfaceView {
-
-    private RenderScriptGL mRS;
-    private FBOSyncRS mRender;
-
-    private ScaleGestureDetector mScaleDetector;
-
-    private static final int INVALID_POINTER_ID = -1;
-    private int mActivePointerId = INVALID_POINTER_ID;
-
-    public FBOSyncView(Context context) {
-        super(context);
-        ensureRenderScript();
-        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
-    }
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRender = new FBOSyncRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        mRender.surfaceChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    public void loadA3DFile(String path) {
-        mRender.loadA3DFile(path);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mScaleDetector.onTouchEvent(ev);
-
-        boolean ret = false;
-        float x = ev.getX();
-        float y = ev.getY();
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN: {
-            mRender.onActionDown(x, y);
-            mActivePointerId = ev.getPointerId(0);
-            ret = true;
-            break;
-        }
-        case MotionEvent.ACTION_MOVE: {
-            if (!mScaleDetector.isInProgress()) {
-                mRender.onActionMove(x, y);
-            }
-            mRender.onActionDown(x, y);
-            ret = true;
-            break;
-        }
-
-        case MotionEvent.ACTION_UP: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_CANCEL: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_POINTER_UP: {
-            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-            final int pointerId = ev.getPointerId(pointerIndex);
-            if (pointerId == mActivePointerId) {
-                // This was our active pointer going up. Choose a new
-                // active pointer and adjust accordingly.
-                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-                x = ev.getX(newPointerIndex);
-                y = ev.getY(newPointerIndex);
-                mRender.onActionDown(x, y);
-                mActivePointerId = ev.getPointerId(newPointerIndex);
-            }
-            break;
-        }
-        }
-
-        return ret;
-    }
-
-    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-        @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            mRender.onActionScale(detector.getScaleFactor());
-            return true;
-        }
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTest.java b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTest.java
deleted file mode 100644
index 72aa3ea..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fbotest;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.MenuInflater;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-import android.net.Uri;
-
-import java.lang.Runtime;
-
-public class FBOTest extends Activity {
-
-    private FBOTestView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new FBOTestView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.pause();
-    }
-}
-
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTestRS.java b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTestRS.java
deleted file mode 100644
index 9e30c4b5..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTestRS.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fbotest;
-
-import java.io.Writer;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.Element.DataType;
-import android.renderscript.Element.DataKind;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.renderscript.Type.Builder;
-import android.util.Log;
-
-
-public class FBOTestRS {
-
-    public FBOTestRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    public void surfaceChanged() {
-        mRS.getWidth();
-        mRS.getHeight();
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Sampler mSampler;
-    private ProgramStore mPSBackground;
-    private ProgramFragment mPFBackground;
-    private ProgramVertex mPVBackground;
-    private ProgramVertexFixedFunction.Constants mPVA;
-
-    private Allocation mGridImage;
-    private Allocation mOffscreen;
-    private Allocation mOffscreenDepth;
-    private Allocation mAllocPV;
-
-    private Font mItalic;
-    private Allocation mTextAlloc;
-
-    private ScriptField_MeshInfo mMeshes;
-    private ScriptC_fbotest mScript;
-
-
-    public void onActionDown(float x, float y) {
-        mScript.invoke_onActionDown(x, y);
-    }
-
-    public void onActionScale(float scale) {
-        mScript.invoke_onActionScale(scale);
-    }
-
-    public void onActionMove(float x, float y) {
-        mScript.invoke_onActionMove(x, y);
-    }
-
-    private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
-
-        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
-        b.setDitherEnabled(false);
-        b.setDepthMaskEnabled(true);
-        mPSBackground = b.create();
-
-        mScript.set_gPFSBackground(mPSBackground);
-    }
-
-    private void initPF() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.CLAMP);
-        mSampler = bs.create();
-
-        ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
-        b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                     ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mPFBackground = b.create();
-        mPFBackground.bindSampler(mSampler, 0);
-
-        mScript.set_gPFBackground(mPFBackground);
-    }
-
-    private void initPV() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mPVBackground = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVA);
-
-        mScript.set_gPVBackground(mPVBackground);
-    }
-
-    private void loadImage() {
-        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
-                                                         Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                         Allocation.USAGE_GRAPHICS_TEXTURE);
-        mScript.set_gTGrid(mGridImage);
-    }
-
-    private void initTextAllocation(String fileName) {
-        String allocString = "Displaying file: " + fileName;
-        mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT);
-        mScript.set_gTextAlloc(mTextAlloc);
-    }
-
-    private void initMeshes(FileA3D model) {
-        int numEntries = model.getIndexEntryCount();
-        int numMeshes = 0;
-        for (int i = 0; i < numEntries; i ++) {
-            FileA3D.IndexEntry entry = model.getIndexEntry(i);
-            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                numMeshes ++;
-            }
-        }
-
-        if (numMeshes > 0) {
-            mMeshes = new ScriptField_MeshInfo(mRS, numMeshes);
-
-            for (int i = 0; i < numEntries; i ++) {
-                FileA3D.IndexEntry entry = model.getIndexEntry(i);
-                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                    Mesh mesh = entry.getMesh();
-                    mMeshes.set_mMesh(i, mesh, false);
-                    mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false);
-                }
-            }
-            mMeshes.copyAll();
-        } else {
-            throw new RSRuntimeException("No valid meshes in file");
-        }
-
-        mScript.bind_gMeshes(mMeshes);
-        mScript.invoke_updateMeshInfo();
-    }
-
-    public void loadA3DFile(String path) {
-        FileA3D model = FileA3D.createFromFile(mRS, path);
-        initMeshes(model);
-        initTextAllocation(path);
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_fbotest(mRS, mRes, R.raw.fbotest);
-
-        initPFS();
-        initPF();
-        initPV();
-
-        loadImage();
-
-        Type.Builder b = new Type.Builder(mRS, Element.RGBA_8888(mRS));
-        b.setX(512).setY(512);
-        mOffscreen = Allocation.createTyped(mRS,
-                                            b.create(),
-                                            Allocation.USAGE_GRAPHICS_TEXTURE |
-                                            Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gOffscreen(mOffscreen);
-
-        b = new Type.Builder(mRS,
-                             Element.createPixel(mRS, DataType.UNSIGNED_16,
-                             DataKind.PIXEL_DEPTH));
-        b.setX(512).setY(512);
-        mOffscreenDepth = Allocation.createTyped(mRS,
-                                                 b.create(),
-                                                 Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gOffscreenDepth(mOffscreenDepth);
-
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
-        initMeshes(model);
-
-        mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
-        mScript.set_gItalic(mItalic);
-
-        initTextAllocation("R.raw.robot");
-
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTestView.java b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTestView.java
deleted file mode 100644
index c9598ee..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/FBOTestView.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fbotest;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.ScaleGestureDetector;
-import android.util.Log;
-
-public class FBOTestView extends RSSurfaceView {
-
-    private RenderScriptGL mRS;
-    private FBOTestRS mRender;
-
-    private ScaleGestureDetector mScaleDetector;
-
-    private static final int INVALID_POINTER_ID = -1;
-    private int mActivePointerId = INVALID_POINTER_ID;
-
-    public FBOTestView(Context context) {
-        super(context);
-        ensureRenderScript();
-        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
-    }
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRender = new FBOTestRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        mRender.surfaceChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    public void loadA3DFile(String path) {
-        mRender.loadA3DFile(path);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mScaleDetector.onTouchEvent(ev);
-
-        boolean ret = false;
-        float x = ev.getX();
-        float y = ev.getY();
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN: {
-            mRender.onActionDown(x, y);
-            mActivePointerId = ev.getPointerId(0);
-            ret = true;
-            break;
-        }
-        case MotionEvent.ACTION_MOVE: {
-            if (!mScaleDetector.isInProgress()) {
-                mRender.onActionMove(x, y);
-            }
-            mRender.onActionDown(x, y);
-            ret = true;
-            break;
-        }
-
-        case MotionEvent.ACTION_UP: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_CANCEL: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_POINTER_UP: {
-            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-            final int pointerId = ev.getPointerId(pointerIndex);
-            if (pointerId == mActivePointerId) {
-                // This was our active pointer going up. Choose a new
-                // active pointer and adjust accordingly.
-                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-                x = ev.getX(newPointerIndex);
-                y = ev.getY(newPointerIndex);
-                mRender.onActionDown(x, y);
-                mActivePointerId = ev.getPointerId(newPointerIndex);
-            }
-            break;
-        }
-        }
-
-        return ret;
-    }
-
-    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-        @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            mRender.onActionScale(detector.getScaleFactor());
-            return true;
-        }
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs
deleted file mode 100644
index 0c177ef..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbosync.rs
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.fbotest)
-
-#include "rs_graphics.rsh"
-
-rs_program_vertex gPVBackground;
-rs_program_fragment gPFBackground;
-
-rs_allocation gTGrid;
-
-rs_program_store gPFSBackground;
-
-rs_font gItalic;
-rs_allocation gTextAlloc;
-
-rs_allocation gOffscreen;
-rs_allocation gOffscreenDepth;
-rs_allocation gReadBackTest;
-
-typedef struct MeshInfo {
-    rs_mesh mMesh;
-    int mNumIndexSets;
-    float3 bBoxMin;
-    float3 bBoxMax;
-} MeshInfo_t;
-
-MeshInfo_t *gMeshes;
-
-static float3 gLookAt;
-
-static float gRotateX;
-static float gRotateY;
-static float gZoom;
-
-static float gLastX;
-static float gLastY;
-
-void onActionDown(float x, float y) {
-    gLastX = x;
-    gLastY = y;
-}
-
-void onActionScale(float scale) {
-
-    gZoom *= 1.0f / scale;
-    gZoom = max(0.1f, min(gZoom, 500.0f));
-}
-
-void onActionMove(float x, float y) {
-    float dx = gLastX - x;
-    float dy = gLastY - y;
-
-    if (fabs(dy) <= 2.0f) {
-        dy = 0.0f;
-    }
-    if (fabs(dx) <= 2.0f) {
-        dx = 0.0f;
-    }
-
-    gRotateY -= dx;
-    if (gRotateY > 360) {
-        gRotateY -= 360;
-    }
-    if (gRotateY < 0) {
-        gRotateY += 360;
-    }
-
-    gRotateX -= dy;
-    gRotateX = min(gRotateX, 80.0f);
-    gRotateX = max(gRotateX, -80.0f);
-
-    gLastX = x;
-    gLastY = y;
-}
-
-void init() {
-    gRotateX = 0.0f;
-    gRotateY = 0.0f;
-    gZoom = 50.0f;
-    gLookAt = 0.0f;
-}
-
-void updateMeshInfo() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    float minX, minY, minZ, maxX, maxY, maxZ;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgMeshComputeBoundingBox(info->mMesh,
-                                  &minX, &minY, &minZ,
-                                  &maxX, &maxY, &maxZ);
-        info->bBoxMin = (float3){minX, minY, minZ};
-        info->bBoxMax = (float3){maxX, maxY, maxZ};
-        gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f;
-    }
-    gLookAt = gLookAt / (float)size;
-}
-
-static void renderAllMeshes() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgDrawMesh(info->mMesh);
-    }
-}
-
-static void drawDescription() {
-    uint height = rsgGetHeight();
-    int left = 0, right = 0, top = 0, bottom = 0;
-
-    rsgBindFont(gItalic);
-
-    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
-    rsgDrawText(gTextAlloc, 2 -left, height - 2 + bottom);
-}
-
-static void renderOffscreen(bool useDepth) {
-
-    rsgBindColorTarget(gOffscreen, 0);
-    if (useDepth) {
-        rsgBindDepthTarget(gOffscreenDepth);
-        rsgClearDepth(1.0f);
-    } else {
-        rsgClearDepthTarget();
-    }
-    rsgClearColor(0.8f, 0.0f, 0.0f, 1.0f);
-
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float)rsAllocationGetDimX(gOffscreen) / (float)rsAllocationGetDimY(gOffscreen);
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindProgramStore(gPFSBackground);
-    rsgBindTexture(gPFBackground, 0, gTGrid);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    // Position our models on the screen
-    rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
-    rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    renderAllMeshes();
-
-    // Render into the frambuffer
-    rsgClearAllRenderTargets();
-}
-
-static void drawOffscreenResult(int posX, int posY, rs_allocation texture) {
-    // display the result
-    rs_matrix4x4 proj, matrix;
-    rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-    rsgBindTexture(gPFBackground, 0, texture);
-    float startX = posX, startY = posY;
-    float width = 256, height = 256;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
-                         startX, startY + height, 0, 0, 0,
-                         startX + width, startY + height, 0, 1, 0,
-                         startX + width, startY, 0, 1, 1);
-}
-
-int root(void) {
-
-    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgClearDepth(1.0f);
-
-    renderOffscreen(true);
-    drawOffscreenResult(0, 0, gOffscreen);
-
-
-    uint32_t w = rsAllocationGetDimX(gOffscreen);
-    uint32_t h = rsAllocationGetDimY(gOffscreen);
-
-    rsgAllocationSyncAll(gOffscreen, RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET);
-
-    rsAllocationCopy2DRange(gReadBackTest, 0, 0, 0,
-                            RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, w, h,
-                            gOffscreen, 0, 0, 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X);
-
-    rsgAllocationSyncAll(gReadBackTest);
-    drawOffscreenResult(0, 300, gReadBackTest);
-
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindProgramStore(gPFSBackground);
-    rsgBindTexture(gPFBackground, 0, gTGrid);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    // Position our models on the screen
-    rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
-    rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    renderAllMeshes();
-
-    drawDescription();
-
-    return 0;
-}
diff --git a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs b/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs
deleted file mode 100644
index 13a3c85..0000000
--- a/tests/RenderScriptTests/FBOTest/src/com/android/fbotest/fbotest.rs
+++ /dev/null
@@ -1,219 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.fbotest)
-
-#include "rs_graphics.rsh"
-
-rs_program_vertex gPVBackground;
-rs_program_fragment gPFBackground;
-
-rs_allocation gTGrid;
-
-rs_program_store gPFSBackground;
-
-rs_font gItalic;
-rs_allocation gTextAlloc;
-
-rs_allocation gOffscreen;
-rs_allocation gOffscreenDepth;
-
-typedef struct MeshInfo {
-    rs_mesh mMesh;
-    int mNumIndexSets;
-    float3 bBoxMin;
-    float3 bBoxMax;
-} MeshInfo_t;
-
-MeshInfo_t *gMeshes;
-
-static float3 gLookAt;
-
-static float gRotateX;
-static float gRotateY;
-static float gZoom;
-
-static float gLastX;
-static float gLastY;
-
-void onActionDown(float x, float y) {
-    gLastX = x;
-    gLastY = y;
-}
-
-void onActionScale(float scale) {
-
-    gZoom *= 1.0f / scale;
-    gZoom = max(0.1f, min(gZoom, 500.0f));
-}
-
-void onActionMove(float x, float y) {
-    float dx = gLastX - x;
-    float dy = gLastY - y;
-
-    if (fabs(dy) <= 2.0f) {
-        dy = 0.0f;
-    }
-    if (fabs(dx) <= 2.0f) {
-        dx = 0.0f;
-    }
-
-    gRotateY -= dx;
-    if (gRotateY > 360) {
-        gRotateY -= 360;
-    }
-    if (gRotateY < 0) {
-        gRotateY += 360;
-    }
-
-    gRotateX -= dy;
-    gRotateX = min(gRotateX, 80.0f);
-    gRotateX = max(gRotateX, -80.0f);
-
-    gLastX = x;
-    gLastY = y;
-}
-
-void init() {
-    gRotateX = 0.0f;
-    gRotateY = 0.0f;
-    gZoom = 50.0f;
-    gLookAt = 0.0f;
-}
-
-void updateMeshInfo() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    float minX, minY, minZ, maxX, maxY, maxZ;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgMeshComputeBoundingBox(info->mMesh,
-                                  &minX, &minY, &minZ,
-                                  &maxX, &maxY, &maxZ);
-        info->bBoxMin = (float3){minX, minY, minZ};
-        info->bBoxMax = (float3){maxX, maxY, maxZ};
-        gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f;
-    }
-    gLookAt = gLookAt / (float)size;
-}
-
-static void renderAllMeshes() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgDrawMesh(info->mMesh);
-    }
-}
-
-static void drawDescription() {
-    uint height = rsgGetHeight();
-    int left = 0, right = 0, top = 0, bottom = 0;
-
-    rsgBindFont(gItalic);
-
-    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
-    rsgDrawText(gTextAlloc, 2 -left, height - 2 + bottom);
-}
-
-static void renderOffscreen(bool useDepth) {
-
-    rsgBindColorTarget(gOffscreen, 0);
-    if (useDepth) {
-        rsgBindDepthTarget(gOffscreenDepth);
-        rsgClearDepth(1.0f);
-    } else {
-        rsgClearDepthTarget();
-    }
-    rsgClearColor(0.8f, 0.8f, 0.8f, 1.0f);
-
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float)rsAllocationGetDimX(gOffscreen) / (float)rsAllocationGetDimY(gOffscreen);
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindProgramStore(gPFSBackground);
-    rsgBindTexture(gPFBackground, 0, gTGrid);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    // Position our models on the screen
-    rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
-    rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    renderAllMeshes();
-
-    // Render into the frambuffer
-    rsgClearAllRenderTargets();
-}
-
-static void drawOffscreenResult(int posX, int posY) {
-    // display the result
-    rs_matrix4x4 proj, matrix;
-    rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-    rsgBindTexture(gPFBackground, 0, gOffscreen);
-    float startX = posX, startY = posY;
-    float width = 256, height = 256;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
-                         startX, startY + height, 0, 0, 0,
-                         startX + width, startY + height, 0, 1, 0,
-                         startX + width, startY, 0, 1, 1);
-}
-
-int root(void) {
-
-    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgClearDepth(1.0f);
-
-    renderOffscreen(true);
-    drawOffscreenResult(0, 0);
-
-    renderOffscreen(false);
-    drawOffscreenResult(0, 256);
-
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindProgramStore(gPFSBackground);
-    rsgBindTexture(gPFBackground, 0, gTGrid);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    // Position our models on the screen
-    rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
-    rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    renderAllMeshes();
-
-    drawDescription();
-
-    return 0;
-}
diff --git a/tests/RenderScriptTests/HelloWorld/Android.mk b/tests/RenderScriptTests/HelloWorld/Android.mk
deleted file mode 100644
index c1c08ec..0000000
--- a/tests/RenderScriptTests/HelloWorld/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := RsHelloWorld
-
-LOCAL_SDK_VERSION := 17
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/HelloWorld/AndroidManifest.xml b/tests/RenderScriptTests/HelloWorld/AndroidManifest.xml
deleted file mode 100644
index 1d37dc9..0000000
--- a/tests/RenderScriptTests/HelloWorld/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
- 
-          http://www.apache.org/licenses/LICENSE-2.0
- 
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.rs.helloworld">
-    <uses-sdk android:minSdkVersion="11" />
-    <application android:label="RsHelloWorld"
-    android:icon="@drawable/test_pattern">
-        <activity android:name="HelloWorld"
-                  android:label="RsHelloWorld"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/RenderScriptTests/HelloWorld/_index.html b/tests/RenderScriptTests/HelloWorld/_index.html
deleted file mode 100644
index 4cab738..0000000
--- a/tests/RenderScriptTests/HelloWorld/_index.html
+++ /dev/null
@@ -1 +0,0 @@
-<p>A Renderscript graphics application that draws the text "Hello, World!" where the user touches.</p>
\ No newline at end of file
diff --git a/tests/RenderScriptTests/HelloWorld/res/drawable/test_pattern.png b/tests/RenderScriptTests/HelloWorld/res/drawable/test_pattern.png
deleted file mode 100644
index e7d1455..0000000
--- a/tests/RenderScriptTests/HelloWorld/res/drawable/test_pattern.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorld.java b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorld.java
deleted file mode 100644
index 9b1697b..0000000
--- a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorld.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.helloworld;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-// Renderscript activity
-public class HelloWorld extends Activity {
-
-    // Custom view to use with RenderScript
-    private HelloWorldView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our view and set it as the content of our Activity
-        mView = new HelloWorldView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally an app should implement onResume() and onPause()
-        // to take appropriate action when the activity loses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally an app should implement onResume() and onPause()
-        // to take appropriate action when the activity loses focus
-        super.onPause();
-        mView.pause();
-    }
-
-}
-
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldRS.java b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldRS.java
deleted file mode 100644
index 4316411..0000000
--- a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldRS.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.helloworld;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-
-// This is the renderer for the HelloWorldView
-public class HelloWorldRS {
-    private Resources mRes;
-    private RenderScriptGL mRS;
-
-    private ScriptC_helloworld mScript;
-
-    public HelloWorldRS() {
-    }
-
-    // This provides us with the renderscript context and resources that
-    // allow us to create the script that does rendering
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    public void onActionDown(int x, int y) {
-        mScript.set_gTouchX(x);
-        mScript.set_gTouchY(y);
-    }
-
-    private void initRS() {
-        mScript = new ScriptC_helloworld(mRS, mRes, R.raw.helloworld);
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldView.java b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldView.java
deleted file mode 100644
index 557ebc5..0000000
--- a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/HelloWorldView.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.helloworld;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-public class HelloWorldView extends RSSurfaceView {
-    // Renderscipt context
-    private RenderScriptGL mRS;
-    // Script that does the rendering
-    private HelloWorldRS mRender;
-
-    public HelloWorldView(Context context) {
-        super(context);
-        ensureRenderScript();
-    }
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            // Initialize renderscript with desired surface characteristics.
-            // In this case, just use the defaults
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            mRS = createRenderScriptGL(sc);
-            // Create an instance of the script that does the rendering
-            mRender = new HelloWorldRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        // Handle the system event and clean up
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        // Pass touch events from the system to the rendering script
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
-            return true;
-        }
-
-        return false;
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/helloworld.rs b/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/helloworld.rs
deleted file mode 100644
index bcf624e..0000000
--- a/tests/RenderScriptTests/HelloWorld/src/com/example/android/rs/helloworld/helloworld.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-// Tell which java package name the reflected files should belong to
-#pragma rs java_package_name(com.example.android.rs.helloworld)
-
-// Built-in header with graphics API's
-#include "rs_graphics.rsh"
-
-// gTouchX and gTouchY are variables that will be reflected for use
-// by the java API. We can use them to notify the script of touch events.
-int gTouchX;
-int gTouchY;
-
-// This is invoked automatically when the script is created
-void init() {
-    gTouchX = 50.0f;
-    gTouchY = 50.0f;
-}
-
-int root(void) {
-
-    // Clear the background color
-    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-    // Tell the runtime what the font color should be
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    // Introuduce ourselves to the world by drawing a greeting
-    // at the position user touched on the screen
-    rsgDrawText("Hello World!", gTouchX, gTouchY);
-
-    // Return value tells RS roughly how often to redraw
-    // in this case 20 ms
-    return 20;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/Android.mk b/tests/RenderScriptTests/MiscSamples/Android.mk
deleted file mode 100644
index ee3567b..0000000
--- a/tests/RenderScriptTests/MiscSamples/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := RsMiscSamples
-
-LOCAL_SDK_VERSION := 17
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/MiscSamples/AndroidManifest.xml b/tests/RenderScriptTests/MiscSamples/AndroidManifest.xml
deleted file mode 100644
index 08a3976..0000000
--- a/tests/RenderScriptTests/MiscSamples/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.rs.miscsamples">
-    <uses-sdk android:minSdkVersion="11" />
-    <application android:label="RsMiscSamples"
-    android:icon="@drawable/test_pattern">
-        <activity android:name="RsList"
-                  android:label="RsList"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        
-        <activity android:name="RsRenderStates"
-                  android:label="RsStates"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/RenderScriptTests/MiscSamples/_index.html b/tests/RenderScriptTests/MiscSamples/_index.html
deleted file mode 100644
index 5872431..0000000
--- a/tests/RenderScriptTests/MiscSamples/_index.html
+++ /dev/null
@@ -1 +0,0 @@
-<p>A set of samples that demonstrate how to use various features of the Renderscript APIs.</p>
\ No newline at end of file
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/checker.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/checker.png
deleted file mode 100644
index b631e1e..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/checker.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/cubemap_test.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/cubemap_test.png
deleted file mode 100644
index baf35d0..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/cubemap_test.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/data.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/data.png
deleted file mode 100644
index 8e34714..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/data.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/leaf.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/leaf.png
deleted file mode 100644
index 3cd3775..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/leaf.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/test_pattern.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/test_pattern.png
deleted file mode 100644
index e7d1455..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/test_pattern.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/torusmap.png b/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/torusmap.png
deleted file mode 100644
index 1e08f3b..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/drawable-nodpi/torusmap.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/multitexf.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/multitexf.glsl
deleted file mode 100644
index e492a47..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/multitexf.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col0 = texture2D(UNI_Tex0, t0).rgba;
-   lowp vec4 col1 = texture2D(UNI_Tex1, t0*4.0).rgba;
-   lowp vec4 col2 = texture2D(UNI_Tex2, t0).rgba;
-   col0.xyz = col0.xyz*col1.xyz*1.5;
-   col0.xyz = mix(col0.xyz, col2.xyz, col2.w);
-   col0.w = 0.5;
-   gl_FragColor = col0;
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shader2f.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shader2f.glsl
deleted file mode 100644
index 5fc05f1..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shader2f.glsl
+++ /dev/null
@@ -1,29 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(-varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = normalize(UNI_light0_Posision.xyz - varWorldPos);
-   vec3 light0R = -reflect(light0Vec, worldNorm);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
-   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
-   float light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
-
-   vec3 light1Vec = normalize(UNI_light1_Posision.xyz - varWorldPos);
-   vec3 light1R = reflect(light1Vec, worldNorm);
-   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
-   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
-   float light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor.xyz + light1_Diffuse * UNI_light1_DiffuseColor.xyz);
-   col.xyz += light0_Specular * UNI_light0_SpecularColor.xyz;
-   col.xyz += light1_Specular * UNI_light1_SpecularColor.xyz;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shader2movev.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shader2movev.glsl
deleted file mode 100644
index a2c807e..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shader2movev.glsl
+++ /dev/null
@@ -1,21 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 objPos = ATTRIB_position;
-   vec3 oldPos = objPos.xyz;
-   objPos.xyz += 0.1*sin(objPos.xyz*2.0 + UNI_time);
-   objPos.xyz += 0.05*sin(objPos.xyz*4.0 + UNI_time*0.5);
-   objPos.xyz += 0.02*sin(objPos.xyz*7.0 + UNI_time*0.75);
-   vec4 worldPos = UNI_model * objPos;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * (ATTRIB_normal + oldPos - objPos.xyz);
-
-   varWorldPos = worldPos.xyz;
-   varWorldNormal = worldNorm;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shader2v.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shader2v.glsl
deleted file mode 100644
index e6885a3..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shader2v.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 objPos = ATTRIB_position;
-   vec4 worldPos = UNI_model * objPos;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * ATTRIB_normal;
-
-   varWorldPos = worldPos.xyz;
-   varWorldNormal = worldNorm;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayf.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayf.glsl
deleted file mode 100644
index 238ecad..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayf.glsl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-varying lowp float light0_Diffuse;
-varying lowp float light0_Specular;
-varying lowp float light1_Diffuse;
-varying lowp float light1_Specular;
-varying vec2 varTex0;
-
-void main() {
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_light_DiffuseColor[0].xyz + light1_Diffuse * UNI_light_DiffuseColor[1].xyz);
-   col.xyz += light0_Specular * UNI_light_SpecularColor[0].xyz;
-   col.xyz += light1_Specular * UNI_light_SpecularColor[1].xyz;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayv.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayv.glsl
deleted file mode 100644
index 7a1310a..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shaderarrayv.glsl
+++ /dev/null
@@ -1,32 +0,0 @@
-varying float light0_Diffuse;
-varying float light0_Specular;
-varying float light1_Diffuse;
-varying float light1_Specular;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 worldPos = UNI_model[0] * ATTRIB_position;
-   worldPos = UNI_model[1] * worldPos;
-   gl_Position = UNI_proj * worldPos;
-
-   mat4 model0 = UNI_model[0];
-   mat3 model3 = mat3(model0[0].xyz, model0[1].xyz, model0[2].xyz);
-   vec3 worldNorm = model3 * ATTRIB_normal;
-   vec3 V = normalize(-worldPos.xyz);
-
-   vec3 light0Vec = normalize(UNI_light_Posision[0].xyz - worldPos.xyz);
-   vec3 light0R = -reflect(light0Vec, worldNorm);
-   light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light_Diffuse[0];
-   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
-   light0_Specular = pow(light0Spec, UNI_light_CosinePower[0]) * UNI_light_Specular[0];
-
-   vec3 light1Vec = normalize(UNI_light_Posision[1].xyz - worldPos.xyz);
-   vec3 light1R = reflect(light1Vec, worldNorm);
-   light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light_Diffuse[1];
-   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
-   light1_Specular = pow(light1Spec, UNI_light_CosinePower[1]) * UNI_light_Specular[1];
-
-   gl_PointSize = 1.0;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shadercubef.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shadercubef.glsl
deleted file mode 100644
index 15696a4..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shadercubef.glsl
+++ /dev/null
@@ -1,8 +0,0 @@
-
-varying vec3 worldNormal;
-
-void main() {
-   lowp vec4 col = textureCube(UNI_Tex0, worldNormal);
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shadercubev.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shadercubev.glsl
deleted file mode 100644
index 70f5cd6..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shadercubev.glsl
+++ /dev/null
@@ -1,10 +0,0 @@
-varying vec3 worldNormal;
-
-// This is where actual shader code begins
-void main() {
-   vec4 worldPos = UNI_model * ATTRIB_position;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   worldNormal = model3 * ATTRIB_normal;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderf.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderf.glsl
deleted file mode 100644
index d56e203..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shaderf.glsl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-varying lowp float light0_Diffuse;
-varying lowp float light0_Specular;
-varying lowp float light1_Diffuse;
-varying lowp float light1_Specular;
-varying vec2 varTex0;
-
-void main() {
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor.xyz + light1_Diffuse * UNI_light1_DiffuseColor.xyz);
-   col.xyz += light0_Specular * UNI_light0_SpecularColor.xyz;
-   col.xyz += light1_Specular * UNI_light1_SpecularColor.xyz;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/shaderv.glsl b/tests/RenderScriptTests/MiscSamples/res/raw/shaderv.glsl
deleted file mode 100644
index f7d01de..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/shaderv.glsl
+++ /dev/null
@@ -1,30 +0,0 @@
-varying float light0_Diffuse;
-varying float light0_Specular;
-varying float light1_Diffuse;
-varying float light1_Specular;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 worldPos = UNI_model * ATTRIB_position;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * ATTRIB_normal;
-   vec3 V = normalize(-worldPos.xyz);
-
-   vec3 light0Vec = normalize(UNI_light0_Posision.xyz - worldPos.xyz);
-   vec3 light0R = -reflect(light0Vec, worldNorm);
-   light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
-   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
-   light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
-
-   vec3 light1Vec = normalize(UNI_light1_Posision.xyz - worldPos.xyz);
-   vec3 light1R = reflect(light1Vec, worldNorm);
-   light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
-   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
-   light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
-
-   gl_PointSize = 1.0;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/res/raw/torus.a3d b/tests/RenderScriptTests/MiscSamples/res/raw/torus.a3d
deleted file mode 100644
index 0322b01..0000000
--- a/tests/RenderScriptTests/MiscSamples/res/raw/torus.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsList.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsList.java
deleted file mode 100644
index dade3b3..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsList.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.miscsamples;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class RsList extends Activity {
-
-    private RsListView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new RsListView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity loses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity loses focus
-        super.onPause();
-        mView.pause();
-    }
-
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListRS.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListRS.java
deleted file mode 100644
index eeb2480..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListRS.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.miscsamples;
-
-import java.io.Writer;
-import java.util.Vector;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.util.Log;
-
-
-public class RsListRS {
-
-    private final int STATE_LAST_FOCUS = 1;
-
-    private static final String[] DATA_LIST = {
-    "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
-    "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
-    "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
-    "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
-    "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
-    "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
-    "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
-    "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
-    "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
-    "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
-    "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
-    "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
-    "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
-    "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
-    "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
-    "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
-    "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
-    "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
-    "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
-    "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
-    "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
-    "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
-    "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
-    "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
-    "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
-    "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
-    "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
-    "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
-    "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
-    "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
-    "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
-    "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
-    "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
-    "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
-    "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
-    "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
-    "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
-    "Ukraine", "United Arab Emirates", "United Kingdom",
-    "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
-    "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
-    "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
-    };
-
-    public RsListRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Font mItalic;
-
-    ScriptField_ListAllocs_s mListAllocs;
-
-    private ScriptC_rslist mScript;
-
-    int mLastX;
-    int mLastY;
-
-    public void onActionDown(int x, int y) {
-        mScript.set_gDY(0.0f);
-
-        mLastX = x;
-        mLastY = y;
-    }
-
-    public void onActionMove(int x, int y) {
-        int dx = mLastX - x;
-        int dy = mLastY - y;
-
-        if (Math.abs(dy) <= 2) {
-            dy = 0;
-        }
-
-        mScript.set_gDY(dy);
-
-        mLastX = x;
-        mLastY = y;
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist);
-
-        mListAllocs = new ScriptField_ListAllocs_s(mRS, DATA_LIST.length);
-        for (int i = 0; i < DATA_LIST.length; i ++) {
-            ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item();
-            listElem.text = Allocation.createFromString(mRS, DATA_LIST[i], Allocation.USAGE_SCRIPT);
-            mListAllocs.set(listElem, i, false);
-        }
-
-        mListAllocs.copyAll();
-
-        mScript.bind_gList(mListAllocs);
-
-        mItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
-        mScript.set_gItalic(mItalic);
-
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListView.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListView.java
deleted file mode 100644
index db6e6c5..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsListView.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.miscsamples;
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-public class RsListView extends RSSurfaceView {
-
-    public RsListView(Context context) {
-        super(context);
-        ensureRenderScript();
-    }
-
-    private RenderScriptGL mRS;
-    private RsListRS mRender;
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            mRS = createRenderScriptGL(sc);
-            mRender = new RsListRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev)
-    {
-        boolean ret = false;
-        int act = ev.getAction();
-        if (act == MotionEvent.ACTION_DOWN) {
-            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
-            ret = true;
-        } else if (act == MotionEvent.ACTION_MOVE) {
-            mRender.onActionMove((int)ev.getX(), (int)ev.getY());
-            ret = true;
-        }
-
-        return ret;
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStates.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStates.java
deleted file mode 100644
index f4ea76e..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStates.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.miscsamples;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class RsRenderStates extends Activity {
-
-    private RsRenderStatesView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new RsRenderStatesView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.pause();
-    }
-
-}
-
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.java
deleted file mode 100644
index 0e319fe..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.miscsamples;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.renderscript.*;
-import android.renderscript.Font.Style;
-import android.renderscript.Program.TextureType;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.renderscript.ProgramStore.BlendSrcFunc;
-import android.renderscript.ProgramStore.BlendDstFunc;
-import android.renderscript.Sampler.Value;
-import android.util.Log;
-
-
-public class RsRenderStatesRS {
-
-    int mWidth;
-    int mHeight;
-
-    public RsRenderStatesRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mWidth = mRS.getWidth();
-        mHeight = mRS.getHeight();
-        mRes = res;
-        mOptionsARGB.inScaled = false;
-        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        mMode = 0;
-        mMaxModes = 0;
-        initRS();
-    }
-
-    public void surfaceChanged() {
-        mWidth = mRS.getWidth();
-        mHeight = mRS.getHeight();
-
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(mWidth, mHeight);
-        mPVA.setProjection(proj);
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-
-    private Sampler mLinearClamp;
-    private Sampler mLinearWrap;
-    private Sampler mMipLinearWrap;
-    private Sampler mNearestClamp;
-    private Sampler mMipLinearAniso8;
-    private Sampler mMipLinearAniso15;
-
-    private ProgramStore mProgStoreBlendNoneDepth;
-    private ProgramStore mProgStoreBlendNone;
-    private ProgramStore mProgStoreBlendAlpha;
-    private ProgramStore mProgStoreBlendAdd;
-
-    private ProgramFragment mProgFragmentTexture;
-    private ProgramFragment mProgFragmentColor;
-
-    private ProgramVertex mProgVertex;
-    private ProgramVertexFixedFunction.Constants mPVA;
-
-    // Custom shaders
-    private ProgramVertex mProgVertexCustom;
-    private ProgramFragment mProgFragmentCustom;
-    private ProgramFragment mProgFragmentMultitex;
-    private ScriptField_VertexShaderConstants_s mVSConst;
-    private ScriptField_VertexShaderConstants2_s mVSConst2;
-    private ScriptField_FragentShaderConstants_s mFSConst;
-    private ScriptField_FragentShaderConstants2_s mFSConst2;
-
-    private ProgramVertex mProgVertexCustom2;
-    private ProgramFragment mProgFragmentCustom2;
-
-    private ProgramVertex mProgVertexCube;
-    private ProgramFragment mProgFragmentCube;
-
-    private ProgramRaster mCullBack;
-    private ProgramRaster mCullFront;
-    private ProgramRaster mCullNone;
-
-    private Allocation mTexTorus;
-    private Allocation mTexOpaque;
-    private Allocation mTexTransparent;
-    private Allocation mTexChecker;
-    private Allocation mTexCube;
-
-    private Mesh mMbyNMesh;
-    private Mesh mTorus;
-
-    Font mFontSans;
-    Font mFontSerif;
-    Font mFontSerifBold;
-    Font mFontSerifItalic;
-    Font mFontSerifBoldItalic;
-    Font mFontMono;
-    private Allocation mTextAlloc;
-
-    private ScriptC_rsrenderstates mScript;
-
-    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
-
-    int mMode;
-    int mMaxModes;
-
-    public void onActionDown(int x, int y) {
-        mMode ++;
-        mMode = mMode % mMaxModes;
-        mScript.set_gDisplayMode(mMode);
-    }
-
-    ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
-        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
-        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
-        builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
-        builder.setDitherEnabled(false);
-        builder.setDepthMaskEnabled(false);
-        return builder.create();
-    }
-
-    private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) {
-
-        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
-                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
-
-        for (int y = 0; y <= hResolution; y++) {
-            final float normalizedY = (float)y / hResolution;
-            final float yOffset = (normalizedY - 0.5f) * height;
-            for (int x = 0; x <= wResolution; x++) {
-                float normalizedX = (float)x / wResolution;
-                float xOffset = (normalizedX - 0.5f) * width;
-                tmb.setTexture(normalizedX, normalizedY);
-                tmb.addVertex(xOffset, yOffset);
-             }
-        }
-
-        for (int y = 0; y < hResolution; y++) {
-            final int curY = y * (wResolution + 1);
-            final int belowY = (y + 1) * (wResolution + 1);
-            for (int x = 0; x < wResolution; x++) {
-                int curV = curY + x;
-                int belowV = belowY + x;
-                tmb.addTriangle(curV, belowV, curV + 1);
-                tmb.addTriangle(belowV, belowV + 1, curV + 1);
-            }
-        }
-
-        return tmb.create(true);
-    }
-
-    private void initProgramStore() {
-        // Use stock the stock program store object
-        mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
-        mProgStoreBlendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(mRS);
-
-        // Create a custom program store
-        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
-        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
-        builder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
-                             ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
-        builder.setDitherEnabled(false);
-        builder.setDepthMaskEnabled(false);
-        mProgStoreBlendAlpha = builder.create();
-
-        mProgStoreBlendAdd = BLEND_ADD_DEPTH_NONE(mRS);
-
-        mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
-        mScript.set_gProgStoreBlendNone(mProgStoreBlendNone);
-        mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha);
-        mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd);
-    }
-
-    private void initProgramFragment() {
-
-        ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                              ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mProgFragmentTexture = texBuilder.create();
-        mProgFragmentTexture.bindSampler(mLinearClamp, 0);
-
-        ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        colBuilder.setVaryingColor(false);
-        mProgFragmentColor = colBuilder.create();
-
-        mScript.set_gProgFragmentColor(mProgFragmentColor);
-        mScript.set_gProgFragmentTexture(mProgFragmentTexture);
-    }
-
-    private void initProgramVertex() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mProgVertex = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mProgVertex).bindConstants(mPVA);
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(mWidth, mHeight);
-        mPVA.setProjection(proj);
-
-        mScript.set_gProgVertex(mProgVertex);
-    }
-
-    private void initCustomShaders() {
-        mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1);
-        mVSConst2 = new ScriptField_VertexShaderConstants2_s(mRS, 1);
-        mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1);
-        mFSConst2 = new ScriptField_FragentShaderConstants2_s(mRS, 1);
-
-        mScript.bind_gVSConstants(mVSConst);
-        mScript.bind_gVSConstants2(mVSConst2);
-        mScript.bind_gFSConstants(mFSConst);
-        mScript.bind_gFSConstants2(mFSConst2);
-
-        // Initialize the shader builder
-        ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS);
-        // Specify the resource that contains the shader string
-        pvbCustom.setShader(mRes, R.raw.shaderv);
-        // Use a script field to spcify the input layout
-        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
-        // Define the constant input layout
-        pvbCustom.addConstant(mVSConst.getAllocation().getType());
-        mProgVertexCustom = pvbCustom.create();
-        // Bind the source of constant data
-        mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0);
-
-        ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS);
-        // Specify the resource that contains the shader string
-        pfbCustom.setShader(mRes, R.raw.shaderf);
-        //Tell the builder how many textures we have
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        // Define the constant input layout
-        pfbCustom.addConstant(mFSConst.getAllocation().getType());
-        mProgFragmentCustom = pfbCustom.create();
-        // Bind the source of constant data
-        mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
-
-        pvbCustom = new ProgramVertex.Builder(mRS);
-        pvbCustom.setShader(mRes, R.raw.shaderarrayv);
-        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
-        pvbCustom.addConstant(mVSConst2.getAllocation().getType());
-        mProgVertexCustom2 = pvbCustom.create();
-        mProgVertexCustom2.bindConstants(mVSConst2.getAllocation(), 0);
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.shaderarrayf);
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        pfbCustom.addConstant(mFSConst2.getAllocation().getType());
-        mProgFragmentCustom2 = pfbCustom.create();
-        mProgFragmentCustom2.bindConstants(mFSConst2.getAllocation(), 0);
-
-        // Cubemap test shaders
-        pvbCustom = new ProgramVertex.Builder(mRS);
-        pvbCustom.setShader(mRes, R.raw.shadercubev);
-        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
-        pvbCustom.addConstant(mVSConst.getAllocation().getType());
-        mProgVertexCube = pvbCustom.create();
-        mProgVertexCube.bindConstants(mVSConst.getAllocation(), 0);
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.shadercubef);
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_CUBE);
-        mProgFragmentCube = pfbCustom.create();
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.multitexf);
-        for (int texCount = 0; texCount < 3; texCount ++) {
-            pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        }
-        mProgFragmentMultitex = pfbCustom.create();
-
-        mScript.set_gProgVertexCustom(mProgVertexCustom);
-        mScript.set_gProgFragmentCustom(mProgFragmentCustom);
-        mScript.set_gProgVertexCustom2(mProgVertexCustom2);
-        mScript.set_gProgFragmentCustom2(mProgFragmentCustom2);
-        mScript.set_gProgVertexCube(mProgVertexCube);
-        mScript.set_gProgFragmentCube(mProgFragmentCube);
-        mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
-    }
-
-    private Allocation loadTextureRGB(int id) {
-        return Allocation.createFromBitmapResource(mRS, mRes, id,
-                                                   Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                   Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    private Allocation loadTextureARGB(int id) {
-        Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
-        return Allocation.createFromBitmap(mRS, b,
-                                           Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                           Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    private void loadImages() {
-        mTexTorus = loadTextureRGB(R.drawable.torusmap);
-        mTexOpaque = loadTextureRGB(R.drawable.data);
-        mTexTransparent = loadTextureARGB(R.drawable.leaf);
-        mTexChecker = loadTextureRGB(R.drawable.checker);
-        Bitmap b = BitmapFactory.decodeResource(mRes, R.drawable.cubemap_test);
-        mTexCube = Allocation.createCubemapFromBitmap(mRS, b);
-
-        mScript.set_gTexTorus(mTexTorus);
-        mScript.set_gTexOpaque(mTexOpaque);
-        mScript.set_gTexTransparent(mTexTransparent);
-        mScript.set_gTexChecker(mTexChecker);
-        mScript.set_gTexCube(mTexCube);
-    }
-
-    private void initFonts() {
-        // Sans font by family name
-        mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
-        mFontSerif = Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8);
-        // Create fonts by family and style
-        mFontSerifBold = Font.create(mRS, mRes, "serif", Font.Style.BOLD, 8);
-        mFontSerifItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
-        mFontSerifBoldItalic = Font.create(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
-        mFontMono = Font.create(mRS, mRes, "mono", Font.Style.NORMAL, 8);
-
-        mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT);
-
-        mScript.set_gFontSans(mFontSans);
-        mScript.set_gFontSerif(mFontSerif);
-        mScript.set_gFontSerifBold(mFontSerifBold);
-        mScript.set_gFontSerifItalic(mFontSerifItalic);
-        mScript.set_gFontSerifBoldItalic(mFontSerifBoldItalic);
-        mScript.set_gFontMono(mFontMono);
-        mScript.set_gTextAlloc(mTextAlloc);
-    }
-
-    private void initMesh() {
-        mMbyNMesh = getMbyNMesh(256, 256, 10, 10);
-        mScript.set_gMbyNMesh(mMbyNMesh);
-
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
-        FileA3D.IndexEntry entry = model.getIndexEntry(0);
-        if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
-            Log.e("rs", "could not load model");
-        } else {
-            mTorus = (Mesh)entry.getObject();
-            mScript.set_gTorusMesh(mTorus);
-        }
-    }
-
-    private void initSamplers() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.WRAP);
-        bs.setWrapT(Sampler.Value.WRAP);
-        mLinearWrap = bs.create();
-
-        mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
-        mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
-        mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS);
-
-        bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR_MIP_LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.WRAP);
-        bs.setWrapT(Sampler.Value.WRAP);
-        bs.setAnisotropy(8.0f);
-        mMipLinearAniso8 = bs.create();
-        bs.setAnisotropy(15.0f);
-        mMipLinearAniso15 = bs.create();
-
-        mScript.set_gLinearClamp(mLinearClamp);
-        mScript.set_gLinearWrap(mLinearWrap);
-        mScript.set_gMipLinearWrap(mMipLinearWrap);
-        mScript.set_gMipLinearAniso8(mMipLinearAniso8);
-        mScript.set_gMipLinearAniso15(mMipLinearAniso15);
-        mScript.set_gNearestClamp(mNearestClamp);
-    }
-
-    private void initProgramRaster() {
-        mCullBack = ProgramRaster.CULL_BACK(mRS);
-        mCullFront = ProgramRaster.CULL_FRONT(mRS);
-        mCullNone = ProgramRaster.CULL_NONE(mRS);
-
-        mScript.set_gCullBack(mCullBack);
-        mScript.set_gCullFront(mCullFront);
-        mScript.set_gCullNone(mCullNone);
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_rsrenderstates(mRS, mRes, R.raw.rsrenderstates);
-
-        mMaxModes = mScript.get_gMaxModes();
-
-        initSamplers();
-        initProgramStore();
-        initProgramFragment();
-        initProgramVertex();
-        initFonts();
-        loadImages();
-        initMesh();
-        initProgramRaster();
-        initCustomShaders();
-
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesView.java b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesView.java
deleted file mode 100644
index a15e38f..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesView.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.rs.miscsamples;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-
-public class RsRenderStatesView extends RSSurfaceView {
-
-    public RsRenderStatesView(Context context) {
-        super(context);
-        ensureRenderScript();
-    }
-
-    private RenderScriptGL mRS;
-    private RsRenderStatesRS mRender;
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRender = new RsRenderStatesRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        mRender.surfaceChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
-            return true;
-        }
-
-        return false;
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rslist.rs b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rslist.rs
deleted file mode 100644
index d9d450d..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rslist.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.example.android.rs.miscsamples)
-
-#include "rs_graphics.rsh"
-
-float gDY;
-
-rs_font gItalic;
-
-typedef struct ListAllocs_s {
-    rs_allocation text;
-} ListAllocs;
-
-ListAllocs *gList;
-
-void init() {
-    gDY = 0.0f;
-}
-
-int textPos = 0;
-
-int root(void) {
-
-    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-    textPos -= (int)gDY*2;
-    gDY *= 0.95;
-
-    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
-    rsgBindFont(gItalic);
-
-    rs_allocation listAlloc;
-    listAlloc = rsGetAllocation(gList);
-    int allocSize = rsAllocationGetDimX(listAlloc);
-
-    int width = rsgGetWidth();
-    int height = rsgGetHeight();
-
-    int itemHeight = 80;
-    int currentYPos = itemHeight + textPos;
-
-    for (int i = 0; i < allocSize; i ++) {
-        if (currentYPos - itemHeight > height) {
-            break;
-        }
-
-        if (currentYPos > 0) {
-            rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0);
-            rsgDrawText(gList[i].text, 30, currentYPos - 32);
-        }
-        currentYPos += itemHeight;
-    }
-
-    return 10;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rsrenderstates.rs b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rsrenderstates.rs
deleted file mode 100644
index 5dabd00..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/rsrenderstates.rs
+++ /dev/null
@@ -1,680 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.example.android.rs.miscsamples)
-
-#include "rs_graphics.rsh"
-#include "shader_def.rsh"
-
-const int gMaxModes = 11;
-
-rs_program_vertex gProgVertex;
-rs_program_fragment gProgFragmentColor;
-rs_program_fragment gProgFragmentTexture;
-
-rs_program_store gProgStoreBlendNoneDepth;
-rs_program_store gProgStoreBlendNone;
-rs_program_store gProgStoreBlendAlpha;
-rs_program_store gProgStoreBlendAdd;
-
-rs_allocation gTexOpaque;
-rs_allocation gTexTorus;
-rs_allocation gTexTransparent;
-rs_allocation gTexChecker;
-rs_allocation gTexCube;
-
-rs_mesh gMbyNMesh;
-rs_mesh gTorusMesh;
-
-rs_font gFontSans;
-rs_font gFontSerif;
-rs_font gFontSerifBold;
-rs_font gFontSerifItalic;
-rs_font gFontSerifBoldItalic;
-rs_font gFontMono;
-rs_allocation gTextAlloc;
-
-int gDisplayMode;
-
-rs_sampler gLinearClamp;
-rs_sampler gLinearWrap;
-rs_sampler gMipLinearWrap;
-rs_sampler gMipLinearAniso8;
-rs_sampler gMipLinearAniso15;
-rs_sampler gNearestClamp;
-
-rs_program_raster gCullBack;
-rs_program_raster gCullFront;
-rs_program_raster gCullNone;
-
-// Custom vertex shader compunents
-VertexShaderConstants *gVSConstants;
-VertexShaderConstants2 *gVSConstants2;
-FragentShaderConstants *gFSConstants;
-FragentShaderConstants2 *gFSConstants2;
-// Export these out to easily set the inputs to shader
-VertexShaderInputs *gVSInputs;
-// Custom shaders we use for lighting
-rs_program_vertex gProgVertexCustom;
-rs_program_fragment gProgFragmentCustom;
-rs_program_vertex gProgVertexCustom2;
-rs_program_fragment gProgFragmentCustom2;
-rs_program_vertex gProgVertexCube;
-rs_program_fragment gProgFragmentCube;
-rs_program_fragment gProgFragmentMultitex;
-
-float gDt = 0;
-
-void init() {
-}
-
-static void displayFontSamples() {
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    int yPos = 100;
-    rsgBindFont(gFontSans);
-    rsgDrawText("Sans font sample", 30, yPos);
-    yPos += 30;
-    rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f);
-    rsgBindFont(gFontSerif);
-    rsgDrawText("Serif font sample", 30, yPos);
-    yPos += 30;
-    rsgFontColor(0.7f, 0.7f, 0.7f, 1.0f);
-    rsgBindFont(gFontSerifBold);
-    rsgDrawText("Serif Bold font sample", 30, yPos);
-    yPos += 30;
-    rsgFontColor(0.5f, 0.5f, 0.9f, 1.0f);
-    rsgBindFont(gFontSerifItalic);
-    rsgDrawText("Serif Italic font sample", 30, yPos);
-    yPos += 30;
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontSerifBoldItalic);
-    rsgDrawText("Serif Bold Italic font sample", 30, yPos);
-    yPos += 30;
-    rsgBindFont(gFontMono);
-    rsgDrawText("Monospace font sample", 30, yPos);
-    yPos += 50;
-
-    // Now use text metrics to center the text
-    uint width = rsgGetWidth();
-    uint height = rsgGetHeight();
-    int left = 0, right = 0, top = 0, bottom = 0;
-
-    rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
-    rsgBindFont(gFontSerifBoldItalic);
-
-    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
-    int centeredPos = width / 2 - (right - left) / 2;
-    rsgDrawText(gTextAlloc, centeredPos, yPos);
-    yPos += 30;
-
-    const char* text = "Centered Text Sample";
-    rsgMeasureText(text, &left, &right, &top, &bottom);
-    centeredPos = width / 2 - (right - left) / 2;
-    rsgDrawText(text, centeredPos, yPos);
-    yPos += 30;
-
-    rsgBindFont(gFontSans);
-    text = "More Centered Text Samples";
-    rsgMeasureText(text, &left, &right, &top, &bottom);
-    centeredPos = width / 2 - (right - left) / 2;
-    rsgDrawText(text, centeredPos, yPos);
-    yPos += 30;
-
-    // Now draw bottom and top right aligned text
-    text = "Top-right aligned text";
-    rsgMeasureText(text, &left, &right, &top, &bottom);
-    rsgDrawText(text, width - right, top);
-
-    text = "Top-left";
-    rsgMeasureText(text, &left, &right, &top, &bottom);
-    rsgDrawText(text, -left, top);
-
-    text = "Bottom-right aligned text";
-    rsgMeasureText(text, &left, &right, &top, &bottom);
-    rsgDrawText(text, width - right, height + bottom);
-
-}
-
-static void bindProgramVertexOrtho() {
-    // Default vertex sahder
-    rsgBindProgramVertex(gProgVertex);
-    // Setup the projectioni matrix
-    rs_matrix4x4 proj;
-    rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-}
-
-static void displayShaderSamples() {
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNone);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
-
-    float startX = 0, startY = 0;
-    float width = 256, height = 256;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1,
-                         startX + width, startY + height, 0, 1, 1,
-                         startX + width, startY, 0, 1, 0);
-
-    startX = 200; startY = 0;
-    width = 128; height = 128;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1,
-                         startX + width, startY + height, 0, 1, 1,
-                         startX + width, startY, 0, 1, 0);
-
-    rsgBindProgramStore(gProgStoreBlendAlpha);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexTransparent);
-    startX = 0; startY = 200;
-    width = 128; height = 128;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1,
-                         startX + width, startY + height, 0, 1, 1,
-                         startX + width, startY, 0, 1, 0);
-
-    // Fragment program with simple color
-    rsgBindProgramFragment(gProgFragmentColor);
-    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.9, 0.3, 0.3, 1);
-    rsgDrawRect(200, 300, 350, 450, 0);
-    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.3, 0.9, 0.3, 1);
-    rsgDrawRect(50, 400, 400, 600, 0);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Texture shader", 10, 50);
-    rsgDrawText("Alpha-blended texture shader", 10, 280);
-    rsgDrawText("Flat color shader", 100, 450);
-}
-
-static void displayBlendingSamples() {
-    int i;
-
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    rsgBindProgramFragment(gProgFragmentColor);
-
-    rsgBindProgramStore(gProgStoreBlendNone);
-    for (i = 0; i < 3; i ++) {
-        float iPlusOne = (float)(i + 1);
-        rsgProgramFragmentConstantColor(gProgFragmentColor,
-                                        0.1f*iPlusOne, 0.2f*iPlusOne, 0.3f*iPlusOne, 1);
-        float yPos = 150 * (float)i;
-        rsgDrawRect(0, yPos, 200, yPos + 200, 0);
-    }
-
-    rsgBindProgramStore(gProgStoreBlendAlpha);
-    for (i = 0; i < 3; i ++) {
-        float iPlusOne = (float)(i + 1);
-        rsgProgramFragmentConstantColor(gProgFragmentColor,
-                                        0.2f*iPlusOne, 0.3f*iPlusOne, 0.1f*iPlusOne, 0.5);
-        float yPos = 150 * (float)i;
-        rsgDrawRect(150, yPos, 350, yPos + 200, 0);
-    }
-
-    rsgBindProgramStore(gProgStoreBlendAdd);
-    for (i = 0; i < 3; i ++) {
-        float iPlusOne = (float)(i + 1);
-        rsgProgramFragmentConstantColor(gProgFragmentColor,
-                                        0.3f*iPlusOne, 0.1f*iPlusOne, 0.2f*iPlusOne, 0.5);
-        float yPos = 150 * (float)i;
-        rsgDrawRect(300, yPos, 500, yPos + 200, 0);
-    }
-
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("No Blending", 10, 50);
-    rsgDrawText("Alpha Blending", 160, 150);
-    rsgDrawText("Additive Blending", 320, 250);
-
-}
-
-static void displayMeshSamples() {
-
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadTranslate(&matrix, 128, 128, 0);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNone);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
-
-    rsgDrawMesh(gMbyNMesh);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("User gen 10 by 10 grid mesh", 10, 250);
-}
-
-static void displayTextureSamplers() {
-
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNone);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
-
-    // Linear clamp
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    float startX = 0, startY = 0;
-    float width = 300, height = 300;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1.1,
-                         startX + width, startY + height, 0, 1.1, 1.1,
-                         startX + width, startY, 0, 1.1, 0);
-
-    // Linear Wrap
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearWrap);
-    startX = 0; startY = 300;
-    width = 300; height = 300;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1.1,
-                         startX + width, startY + height, 0, 1.1, 1.1,
-                         startX + width, startY, 0, 1.1, 0);
-
-    // Nearest
-    rsgBindSampler(gProgFragmentTexture, 0, gNearestClamp);
-    startX = 300; startY = 0;
-    width = 300; height = 300;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1.1,
-                         startX + width, startY + height, 0, 1.1, 1.1,
-                         startX + width, startY, 0, 1.1, 0);
-
-    rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
-    startX = 300; startY = 300;
-    width = 300; height = 300;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1.5,
-                         startX + width, startY + height, 0, 1.5, 1.5,
-                         startX + width, startY, 0, 1.5, 0);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Filtering: linear clamp", 10, 290);
-    rsgDrawText("Filtering: linear wrap", 10, 590);
-    rsgDrawText("Filtering: nearest clamp", 310, 290);
-    rsgDrawText("Filtering: miplinear wrap", 310, 590);
-}
-
-static float gTorusRotation = 0;
-
-static void displayCullingSamples() {
-    rsgBindProgramVertex(gProgVertex);
-    // Setup the projectioni matrix with 60 degree field of view
-    rs_matrix4x4 proj;
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
-
-    // Aplly a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    rs_matrix4x4 matrix;
-    // Position our model on the screen
-    rsMatrixLoadTranslate(&matrix, -2.0f, 0.0f, -10.0f);
-    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-    // Use front face culling
-    rsgBindProgramRaster(gCullFront);
-    rsgDrawMesh(gTorusMesh);
-
-    rsMatrixLoadTranslate(&matrix, 2.0f, 0.0f, -10.0f);
-    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-    rsgDrawMesh(gTorusMesh);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Displaying mesh front/back face culling", 10, rsgGetHeight() - 10);
-}
-
-static float gLight0Rotation = 0;
-static float gLight1Rotation = 0;
-
-static void setupCustomShaderLights() {
-    float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
-    float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
-    float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
-    float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f};
-    float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f};
-    float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f};
-
-    gLight0Rotation += 50.0f * gDt;
-    if (gLight0Rotation > 360.0f) {
-        gLight0Rotation -= 360.0f;
-    }
-    gLight1Rotation -= 50.0f * gDt;
-    if (gLight1Rotation > 360.0f) {
-        gLight1Rotation -= 360.0f;
-    }
-
-    rs_matrix4x4 l0Mat;
-    rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f);
-    light0Pos = rsMatrixMultiply(&l0Mat, light0Pos);
-    rs_matrix4x4 l1Mat;
-    rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f);
-    light1Pos = rsMatrixMultiply(&l1Mat, light1Pos);
-
-    // Set light 0 properties
-    gVSConstants->light0_Posision = light0Pos;
-    gVSConstants->light0_Diffuse = 1.0f;
-    gVSConstants->light0_Specular = 0.5f;
-    gVSConstants->light0_CosinePower = 10.0f;
-    // Set light 1 properties
-    gVSConstants->light1_Posision = light1Pos;
-    gVSConstants->light1_Diffuse = 1.0f;
-    gVSConstants->light1_Specular = 0.7f;
-    gVSConstants->light1_CosinePower = 25.0f;
-    rsgAllocationSyncAll(rsGetAllocation(gVSConstants));
-
-    gVSConstants2->light_Posision[0] = light0Pos;
-    gVSConstants2->light_Diffuse[0] = 1.0f;
-    gVSConstants2->light_Specular[0] = 0.5f;
-    gVSConstants2->light_CosinePower[0] = 10.0f;
-    gVSConstants2->light_Posision[1] = light1Pos;
-    gVSConstants2->light_Diffuse[1] = 1.0f;
-    gVSConstants2->light_Specular[1] = 0.7f;
-    gVSConstants2->light_CosinePower[1] = 25.0f;
-    rsgAllocationSyncAll(rsGetAllocation(gVSConstants2));
-
-    // Update fragmetn shader constants
-    // Set light 0 colors
-    gFSConstants->light0_DiffuseColor = light0DiffCol;
-    gFSConstants->light0_SpecularColor = light0SpecCol;
-    // Set light 1 colors
-    gFSConstants->light1_DiffuseColor = light1DiffCol;
-    gFSConstants->light1_SpecularColor = light1SpecCol;
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
-
-    gFSConstants2->light_DiffuseColor[0] = light0DiffCol;
-    gFSConstants2->light_SpecularColor[0] = light0SpecCol;
-    // Set light 1 colors
-    gFSConstants2->light_DiffuseColor[1] = light1DiffCol;
-    gFSConstants2->light_SpecularColor[1] = light1SpecCol;
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstants2));
-}
-
-static void displayCustomShaderSamples() {
-
-    // Update vertex shader constants
-    // Load model matrix
-    // Aplly a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    // Position our model on the screen
-    rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
-    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-    // Setup the projectioni matrix
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
-    setupCustomShaderLights();
-
-    rsgBindProgramVertex(gProgVertexCustom);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentCustom);
-    rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentCustom, 0, gTexTorus);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-    rsgDrawMesh(gTorusMesh);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
-}
-
-static void displayCustomShaderSamples2() {
-
-    // Update vertex shader constants
-    // Load model matrix
-    // Aplly a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    // Position our model on the screen
-    rsMatrixLoadTranslate(&gVSConstants2->model[1], 0.0f, 0.0f, -10.0f);
-    rsMatrixLoadIdentity(&gVSConstants2->model[0]);
-    rsMatrixRotate(&gVSConstants2->model[0], gTorusRotation, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&gVSConstants2->model[0], gTorusRotation, 0.0f, 0.0f, 1.0f);
-    // Setup the projectioni matrix
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&gVSConstants2->proj, 30.0f, aspect, 0.1f, 100.0f);
-    setupCustomShaderLights();
-
-    rsgBindProgramVertex(gProgVertexCustom2);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentCustom2);
-    rsgBindSampler(gProgFragmentCustom2, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentCustom2, 0, gTexTorus);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-    rsgDrawMesh(gTorusMesh);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Custom shader sample with array uniforms", 10, rsgGetHeight() - 10);
-}
-
-static void displayCubemapShaderSample() {
-    // Update vertex shader constants
-    // Load model matrix
-    // Aplly a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    // Position our model on the screen
-    // Position our model on the screen
-    rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
-    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
-    // Setup the projectioni matrix
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
-
-    rsgBindProgramVertex(gProgVertexCube);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentCube);
-    rsgBindSampler(gProgFragmentCube, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentCube, 0, gTexCube);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-    rsgDrawMesh(gTorusMesh);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Cubemap shader sample", 10, rsgGetHeight() - 10);
-}
-
-static void displayMultitextureSample() {
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNone);
-    rsgBindProgramFragment(gProgFragmentMultitex);
-    rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp);
-    rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap);
-    rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp);
-    rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker);
-    rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus);
-    rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent);
-
-    float startX = 0, startY = 0;
-    float width = 256, height = 256;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 1,
-                         startX + width, startY + height, 0, 1, 1,
-                         startX + width, startY, 0, 1, 0);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    rsgDrawText("Custom shader with multitexturing", 10, 280);
-}
-
-static float gAnisoTime = 0.0f;
-static uint anisoMode = 0;
-static void displayAnisoSample() {
-
-    gAnisoTime += gDt;
-
-    rsgBindProgramVertex(gProgVertex);
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rs_matrix4x4 proj;
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rs_matrix4x4 matrix;
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNone);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -10.0f);
-    rsMatrixRotate(&matrix, -80, 1.0f, 0.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    rsgBindProgramRaster(gCullNone);
-
-    rsgBindTexture(gProgFragmentTexture, 0, gTexChecker);
-
-    if (gAnisoTime >= 5.0f) {
-        gAnisoTime = 0.0f;
-        anisoMode ++;
-        anisoMode = anisoMode % 3;
-    }
-
-    if (anisoMode == 0) {
-        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso8);
-    } else if (anisoMode == 1) {
-        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso15);
-    } else {
-        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
-    }
-
-    float startX = -15;
-    float startY = -15;
-    float width = 30;
-    float height = 30;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                         startX, startY + height, 0, 0, 10,
-                         startX + width, startY + height, 0, 10, 10,
-                         startX + width, startY, 0, 10, 0);
-
-    rsgBindProgramRaster(gCullBack);
-
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgBindFont(gFontMono);
-    if (anisoMode == 0) {
-        rsgDrawText("Anisotropic filtering 8", 10, 40);
-    } else if (anisoMode == 1) {
-        rsgDrawText("Anisotropic filtering 15", 10, 40);
-    } else {
-        rsgDrawText("Miplinear filtering", 10, 40);
-    }
-}
-
-int root(void) {
-
-    gDt = rsGetDt();
-
-    rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
-    rsgClearDepth(1.0f);
-
-    switch (gDisplayMode) {
-    case 0:
-        displayFontSamples();
-        break;
-    case 1:
-        displayShaderSamples();
-        break;
-    case 2:
-        displayBlendingSamples();
-        break;
-    case 3:
-        displayMeshSamples();
-        break;
-    case 4:
-        displayTextureSamplers();
-        break;
-    case 5:
-        displayCullingSamples();
-        break;
-    case 6:
-        displayCustomShaderSamples();
-        break;
-    case 7:
-        displayMultitextureSample();
-        break;
-    case 8:
-        displayAnisoSample();
-        break;
-    case 9:
-        displayCustomShaderSamples2();
-        break;
-    case 10:
-        displayCubemapShaderSample();
-        break;
-    }
-
-    return 10;
-}
diff --git a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/shader_def.rsh b/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/shader_def.rsh
deleted file mode 100644
index 08cf361..0000000
--- a/tests/RenderScriptTests/MiscSamples/src/com/example/android/rs/miscsamples/shader_def.rsh
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.example.android.rs.miscsamples)
-
-typedef struct VertexShaderConstants_s {
-    rs_matrix4x4 model;
-    rs_matrix4x4 proj;
-    float4 light0_Posision;
-    float light0_Diffuse;
-    float light0_Specular;
-    float light0_CosinePower;
-
-    float4 light1_Posision;
-    float light1_Diffuse;
-    float light1_Specular;
-    float light1_CosinePower;
-} VertexShaderConstants;
-
-typedef struct VertexShaderConstants2_s {
-    rs_matrix4x4 model[2];
-    rs_matrix4x4 proj;
-    float4 light_Posision[2];
-    float light_Diffuse[2];
-    float light_Specular[2];
-    float light_CosinePower[2];
-} VertexShaderConstants2;
-
-typedef struct VertexShaderConstants3_s {
-    rs_matrix4x4 model;
-    rs_matrix4x4 proj;
-    float time;
-} VertexShaderConstants3;
-
-
-typedef struct FragentShaderConstants_s {
-    float4 light0_DiffuseColor;
-    float4 light0_SpecularColor;
-
-    float4 light1_DiffuseColor;
-    float4 light1_SpecularColor;
-} FragentShaderConstants;
-
-typedef struct FragentShaderConstants2_s {
-    float4 light_DiffuseColor[2];
-    float4 light_SpecularColor[2];
-} FragentShaderConstants2;
-
-typedef struct FragentShaderConstants3_s {
-    float4 light0_DiffuseColor;
-    float4 light0_SpecularColor;
-    float4 light0_Posision;
-    float light0_Diffuse;
-    float light0_Specular;
-    float light0_CosinePower;
-
-    float4 light1_DiffuseColor;
-    float4 light1_SpecularColor;
-    float4 light1_Posision;
-    float light1_Diffuse;
-    float light1_Specular;
-    float light1_CosinePower;
-} FragentShaderConstants3;
-
-typedef struct VertexShaderInputs_s {
-    float4 position;
-    float3 normal;
-    float2 texture0;
-} VertexShaderInputs;
-
diff --git a/tests/RenderScriptTests/ModelViewer/Android.mk b/tests/RenderScriptTests/ModelViewer/Android.mk
deleted file mode 100644
index 86724cf..0000000
--- a/tests/RenderScriptTests/ModelViewer/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
-
-LOCAL_SDK_VERSION := 17
-
-LOCAL_PACKAGE_NAME := ModelViewer
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ModelViewer/AndroidManifest.xml b/tests/RenderScriptTests/ModelViewer/AndroidManifest.xml
deleted file mode 100644
index 57ec4fe..0000000
--- a/tests/RenderScriptTests/ModelViewer/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.modelviewer">
-    <application android:label="ModelViewer">
-        <activity android:name="SimpleModel"
-                  android:label="SimpleModel"
-                  android:screenOrientation="nosensor">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity android:name="A3DSelector"
-                  android:label="A3DSelector"
-                  android:hardwareAccelerated="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity>
-        <activity android:name="SceneGraph"
-                  android:label="SceneGraph"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
deleted file mode 100644
index f7353fd..0000000
--- a/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ModelViewer/res/menu/loader_menu.xml b/tests/RenderScriptTests/ModelViewer/res/menu/loader_menu.xml
deleted file mode 100644
index 2a8759c..0000000
--- a/tests/RenderScriptTests/ModelViewer/res/menu/loader_menu.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/load_model"
-          android:title="@string/load_model" />
-    <item android:id="@+id/display_options"
-          android:title="@string/display_options" />
-    <item android:id="@+id/sensor"
-          android:title="@string/sensor" />
-</menu>
diff --git a/tests/RenderScriptTests/ModelViewer/res/raw/robot.a3d b/tests/RenderScriptTests/ModelViewer/res/raw/robot.a3d
deleted file mode 100644
index f48895c..0000000
--- a/tests/RenderScriptTests/ModelViewer/res/raw/robot.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ModelViewer/res/values/strings.xml b/tests/RenderScriptTests/ModelViewer/res/values/strings.xml
deleted file mode 100644
index a5c8f22..0000000
--- a/tests/RenderScriptTests/ModelViewer/res/values/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <skip />
-    <string name="load_model">Load Model</string>
-    <string name="display_options">Display Options</string>
-    <string name="sensor">Toggle Sensor</string>
-</resources>
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/A3DSelector.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/A3DSelector.java
deleted file mode 100644
index 0e2004f..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/A3DSelector.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.app.ListActivity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-/**
- * A list view where the last item the user clicked is placed in
- * the "activated" state, causing its background to highlight.
- */
-public class A3DSelector extends ListActivity {
-
-    File[] mCurrentSubList;
-    File mCurrentFile;
-
-    class A3DFilter implements FileFilter {
-        public boolean accept(File file) {
-            if (file.isDirectory()) {
-                return true;
-            }
-            return file.getName().endsWith(".a3d");
-        }
-    }
-
-    private void populateList(File file) {
-
-        mCurrentFile = file;
-        setTitle(mCurrentFile.getAbsolutePath() + "/*.a3d");
-        List<String> names = new ArrayList<String>();
-        names.add("..");
-
-        mCurrentSubList = mCurrentFile.listFiles(new A3DFilter());
-
-        if (mCurrentSubList != null) {
-            for (int i = 0; i < mCurrentSubList.length; i ++) {
-                String fileName = mCurrentSubList[i].getName();
-                if (mCurrentSubList[i].isDirectory()) {
-                    fileName = "/" + fileName;
-                }
-                names.add(fileName);
-            }
-        }
-
-        // Use the built-in layout for showing a list item with a single
-        // line of text whose background is changes when activated.
-        setListAdapter(new ArrayAdapter<String>(this,
-                android.R.layout.simple_list_item_activated_1, names));
-        getListView().setTextFilterEnabled(true);
-
-        // Tell the list view to show one checked/activated item at a time.
-        getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        populateList(new File("/sdcard/"));
-    }
-
-    @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
-        if (position == 0) {
-            File parent = mCurrentFile.getParentFile();
-            if (parent == null) {
-                return;
-            }
-            populateList(parent);
-            return;
-        }
-
-        // the first thing in list is parent directory
-        File selectedFile = mCurrentSubList[position - 1];
-        if (selectedFile.isDirectory()) {
-            populateList(selectedFile);
-            return;
-        }
-
-        Intent resultIntent = new Intent();
-        resultIntent.setData(Uri.fromFile(selectedFile));
-        setResult(RESULT_OK, resultIntent);
-        finish();
-    }
-
-}
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraph.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraph.java
deleted file mode 100644
index c9c4dc1..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraph.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-
-import java.lang.Runtime;
-
-public class SceneGraph extends Activity {
-
-    private SceneGraphView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new SceneGraphView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.pause();
-    }
-
-}
-
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
deleted file mode 100644
index f91f31e..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import java.io.Writer;
-import java.util.Map;
-import java.util.Vector;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.Element.Builder;
-import android.renderscript.Font.Style;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.util.Log;
-
-
-public class SceneGraphRS {
-
-    private final int STATE_LAST_FOCUS = 1;
-
-    int mWidth;
-    int mHeight;
-    int mRotation;
-
-    public SceneGraphRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res, int width, int height) {
-        mRS = rs;
-        mRes = res;
-        mWidth = width;
-        mHeight = height;
-        mRotation = 0;
-        initRS();
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Sampler mSampler;
-    private ProgramStore mPSBackground;
-    private ProgramFragment mPFBackground;
-    private ProgramVertex mPVBackground;
-    private ProgramVertexFixedFunction.Constants mPVA;
-
-    private Allocation mGridImage;
-    private Allocation mAllocPV;
-
-    private Mesh mMesh;
-
-    private Font mItalic;
-    private Allocation mTextAlloc;
-
-    private ScriptC_scenegraph mScript;
-    private ScriptC_transform mTransformScript;
-
-    int mLastX;
-    int mLastY;
-
-    public void touchEvent(int x, int y) {
-        int dx = mLastX - x;
-        if (Math.abs(dx) > 50 || Math.abs(dx) < 3) {
-            dx = 0;
-        }
-
-        mRotation -= dx;
-        if (mRotation > 360) {
-            mRotation -= 360;
-        }
-        if (mRotation < 0) {
-            mRotation += 360;
-        }
-
-        mScript.set_gRotate(-(float)mRotation);
-
-        mLastX = x;
-        mLastY = y;
-    }
-
-    private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
-
-        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
-        b.setDitherEnabled(false);
-        b.setDepthMaskEnabled(true);
-        mPSBackground = b.create();
-
-        mScript.set_gPFSBackground(mPSBackground);
-    }
-
-    private void initPF() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.CLAMP);
-        mSampler = bs.create();
-
-        ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
-        b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                     ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mPFBackground = b.create();
-        mPFBackground.bindSampler(mSampler, 0);
-
-        mScript.set_gPFBackground(mPFBackground);
-    }
-
-    private void initPV() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mPVBackground = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVA);
-
-        mScript.set_gPVBackground(mPVBackground);
-    }
-
-    private void loadImage() {
-        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
-                                                         Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                         Allocation.USAGE_GRAPHICS_TEXTURE);
-        mScript.set_gTGrid(mGridImage);
-    }
-
-    private void initTextAllocation() {
-        String allocString = "Displaying file: R.raw.robot";
-        mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT);
-        mScript.set_gTextAlloc(mTextAlloc);
-    }
-
-    SgTransform mRootTransform;
-    SgTransform mGroup1;
-
-    SgTransform mRobot1;
-    SgTransform mRobot2;
-
-    void initTransformHierarchy() {
-        mRootTransform = new SgTransform(mRS);
-
-        mGroup1 = new SgTransform(mRS);
-        mRootTransform.addChild(mGroup1);
-
-        mRobot1 = new SgTransform(mRS);
-        mRobot2 = new SgTransform(mRS);
-
-        mGroup1.addChild(mRobot1);
-        mGroup1.addChild(mRobot2);
-
-        mGroup1.setTransform(0, new Float4(0.0f, 0.0f, -15.0f, 0.0f), TransformType.TRANSLATE);
-        mGroup1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 15.0f), TransformType.ROTATE);
-
-        mRobot1.setTransform(0, new Float4(-3.0f, -0.5f, 0.0f, 0.0f), TransformType.TRANSLATE);
-        mRobot1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 20.0f), TransformType.ROTATE);
-        mRobot1.setTransform(2, new Float4(0.2f, 0.2f, 0.2f, 0.0f), TransformType.SCALE);
-
-        mRobot2.setTransform(0, new Float4(3.0f, 0.0f, 0.0f, 0.0f), TransformType.TRANSLATE);
-        mRobot2.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, -20.0f), TransformType.ROTATE);
-        mRobot2.setTransform(2, new Float4(0.3f, 0.3f, 0.3f, 0.0f), TransformType.SCALE);
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_scenegraph(mRS, mRes, R.raw.scenegraph);
-        mTransformScript = new ScriptC_transform(mRS, mRes, R.raw.transform);
-        mTransformScript.set_transformScript(mTransformScript);
-
-        mScript.set_gTransformRS(mTransformScript);
-
-        initPFS();
-        initPF();
-        initPV();
-
-        loadImage();
-
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
-        FileA3D.IndexEntry entry = model.getIndexEntry(0);
-        if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
-            Log.e("rs", "could not load model");
-        } else {
-            mMesh = (Mesh)entry.getObject();
-            mScript.set_gTestMesh(mMesh);
-        }
-
-        mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
-        mScript.set_gItalic(mItalic);
-
-        initTextAllocation();
-
-        initTransformHierarchy();
-
-        mScript.bind_gRootNode(mRootTransform.getField());
-
-        mScript.bind_gGroup(mGroup1.mParent.mChildField);
-        mScript.bind_gRobot1(mRobot1.mParent.mChildField);
-        mScript.set_gRobot1Index(mRobot1.mIndexInParentGroup);
-        mScript.bind_gRobot2(mRobot2.mParent.mChildField);
-        mScript.set_gRobot2Index(mRobot2.mIndexInParentGroup);
-
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraphView.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraphView.java
deleted file mode 100644
index 0b6a3b8..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SceneGraphView.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-public class SceneGraphView extends RSSurfaceView {
-
-    public SceneGraphView(Context context) {
-        super(context);
-        //setFocusable(true);
-    }
-
-    private RenderScriptGL mRS;
-    private SceneGraphRS mRender;
-
-
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRS.setSurface(holder, w, h);
-            mRender = new SceneGraphRS();
-            mRender.init(mRS, getResources(), w, h);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event)
-    {
-        // break point at here
-        // this method doesn't work when 'extends View' include 'extends ScrollView'.
-        return super.onKeyDown(keyCode, event);
-    }
-
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev)
-    {
-        boolean ret = true;
-        int act = ev.getAction();
-        if (act == ev.ACTION_UP) {
-            ret = false;
-        }
-
-        mRender.touchEvent((int)ev.getX(), (int)ev.getY());
-        return ret;
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SgTransform.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SgTransform.java
deleted file mode 100644
index f5484e2..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SgTransform.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import java.io.Writer;
-import java.util.Map;
-import java.util.Vector;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.Element.Builder;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.util.Log;
-
-enum TransformType {
-
-    NONE(0),
-    TRANSLATE(1),
-    ROTATE(2),
-    SCALE(3);
-
-    int mID;
-    TransformType(int id) {
-        mID = id;
-    }
-}
-
-public class SgTransform {
-
-
-    ScriptField_SgTransform mTransformField;
-    ScriptField_SgTransform mChildField;
-    public ScriptField_SgTransform.Item mTransformData;
-
-    RenderScript mRS;
-
-    Vector mChildren;
-    SgTransform mParent;
-    int mIndexInParentGroup;
-
-    public void setParent(SgTransform parent, int parentIndex) {
-        mParent = parent;
-        mIndexInParentGroup = parentIndex;
-    }
-
-    public void addChild(SgTransform child) {
-        mChildren.add(child);
-        child.setParent(this, mChildren.size() - 1);
-    }
-
-    public void setTransform(int index, Float4 value, TransformType type) {
-        mTransformData.transforms[index] = value;
-        mTransformData.transformTypes[index] = type.mID;
-    }
-
-    void initData() {
-        int numElements = mTransformData.transforms.length;
-        mTransformData.transformTypes = new int[numElements];
-        for (int i = 0; i < numElements; i ++) {
-            mTransformData.transforms[i] = new Float4(0, 0, 0, 0);
-            mTransformData.transformTypes[i] = TransformType.NONE.mID;
-        }
-
-        mTransformData.isDirty = 1;
-        mTransformData.children = null;
-    }
-
-    public SgTransform(RenderScript rs) {
-        mRS = rs;
-        mTransformData = new ScriptField_SgTransform.Item();
-        mChildren = new Vector();
-        initData();
-    }
-
-    public ScriptField_SgTransform.Item getData() {
-        if (mChildren.size() != 0) {
-            mChildField = new ScriptField_SgTransform(mRS, mChildren.size());
-            mTransformData.children = mChildField.getAllocation();
-
-            for (int i = 0; i < mChildren.size(); i ++) {
-                SgTransform child = (SgTransform)mChildren.get(i);
-                mChildField.set(child.getData(), i, false);
-            }
-            mChildField.copyAll();
-        }
-
-        return mTransformData;
-    }
-
-    public ScriptField_SgTransform getField() {
-        mTransformField = new ScriptField_SgTransform(mRS, 1);
-        mTransformField.set(getData(), 0, true);
-        return mTransformField;
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModel.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModel.java
deleted file mode 100644
index 2b29ff4..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModel.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.MenuInflater;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-import android.net.Uri;
-
-import java.lang.Runtime;
-
-public class SimpleModel extends Activity {
-
-    private SimpleModelView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new SimpleModelView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.pause();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.loader_menu, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        // Handle item selection
-        switch (item.getItemId()) {
-        case R.id.load_model:
-            loadModel();
-            return true;
-        case R.id.display_options:
-            return true;
-        case R.id.sensor:
-            mView.toggleSensor();
-            return true;
-        default:
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private static final int FIND_A3D_MODEL = 10;
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (resultCode == RESULT_OK) {
-            if (requestCode == FIND_A3D_MODEL) {
-                Uri selectedImageUri = data.getData();
-                Log.e("Selected Path: ", selectedImageUri.getPath());
-                mView.loadA3DFile(selectedImageUri.getPath());
-            }
-        }
-    }
-
-    public void loadModel() {
-        Intent intent = new Intent();
-        intent.setAction(Intent.ACTION_PICK);
-        intent.setClassName("com.android.modelviewer",
-                            "com.android.modelviewer.A3DSelector");
-        startActivityForResult(intent, FIND_A3D_MODEL);
-    }
-
-}
-
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
deleted file mode 100644
index 5fa3a9e..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import java.io.Writer;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.util.Log;
-
-
-public class SimpleModelRS {
-
-    public SimpleModelRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    public void surfaceChanged() {
-        mRS.getWidth();
-        mRS.getHeight();
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Sampler mSampler;
-    private ProgramStore mPSBackground;
-    private ProgramFragment mPFBackground;
-    private ProgramVertex mPVBackground;
-    private ProgramVertexFixedFunction.Constants mPVA;
-
-    private Allocation mGridImage;
-    private Allocation mAllocPV;
-
-    private Font mItalic;
-    private Allocation mTextAlloc;
-
-    private ScriptField_MeshInfo mMeshes;
-    private ScriptC_simplemodel mScript;
-
-
-    public void onActionDown(float x, float y) {
-        mScript.invoke_onActionDown(x, y);
-    }
-
-    public void onActionScale(float scale) {
-        mScript.invoke_onActionScale(scale);
-    }
-
-    public void onActionMove(float x, float y) {
-        mScript.invoke_onActionMove(x, y);
-    }
-
-    public void onPostureChanged(Matrix4f posture) {
-        mScript.set_gPostureMatrix(posture);
-    }
-
-    private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
-
-        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
-        b.setDitherEnabled(false);
-        b.setDepthMaskEnabled(true);
-        mPSBackground = b.create();
-
-        mScript.set_gPFSBackground(mPSBackground);
-    }
-
-    private void initPF() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMinification(Sampler.Value.LINEAR);
-        bs.setMagnification(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.CLAMP);
-        mSampler = bs.create();
-
-        ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
-        b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                     ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mPFBackground = b.create();
-        mPFBackground.bindSampler(mSampler, 0);
-
-        mScript.set_gPFBackground(mPFBackground);
-    }
-
-    private void initPV() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mPVBackground = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mPVBackground).bindConstants(mPVA);
-
-        mScript.set_gPVBackground(mPVBackground);
-    }
-
-    private void loadImage() {
-        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
-                                                         Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                         Allocation.USAGE_GRAPHICS_TEXTURE);
-        mScript.set_gTGrid(mGridImage);
-    }
-
-    private void initTextAllocation(String fileName) {
-        String allocString = "Displaying file: " + fileName;
-        mTextAlloc = Allocation.createFromString(mRS, allocString, Allocation.USAGE_SCRIPT);
-        mScript.set_gTextAlloc(mTextAlloc);
-    }
-
-    private void initMeshes(FileA3D model) {
-        int numEntries = model.getIndexEntryCount();
-        int numMeshes = 0;
-        for (int i = 0; i < numEntries; i ++) {
-            FileA3D.IndexEntry entry = model.getIndexEntry(i);
-            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                numMeshes ++;
-            }
-        }
-
-        if (numMeshes > 0) {
-            mMeshes = new ScriptField_MeshInfo(mRS, numMeshes);
-
-            for (int i = 0; i < numEntries; i ++) {
-                FileA3D.IndexEntry entry = model.getIndexEntry(i);
-                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                    Mesh mesh = entry.getMesh();
-                    mMeshes.set_mMesh(i, mesh, false);
-                    mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false);
-                }
-            }
-            mMeshes.copyAll();
-        } else {
-            throw new RSRuntimeException("No valid meshes in file");
-        }
-
-        mScript.bind_gMeshes(mMeshes);
-        mScript.invoke_updateMeshInfo();
-    }
-
-    public void loadA3DFile(String path) {
-        FileA3D model = FileA3D.createFromFile(mRS, path);
-        initMeshes(model);
-        initTextAllocation(path);
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_simplemodel(mRS, mRes, R.raw.simplemodel);
-
-        initPFS();
-        initPF();
-        initPV();
-
-        loadImage();
-
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
-        initMeshes(model);
-
-        mItalic = Font.create(mRS, mRes, "serif", Font.Style.ITALIC, 8);
-        mScript.set_gItalic(mItalic);
-
-        initTextAllocation("R.raw.robot");
-
-        mRS.bindRootScript(mScript);
-    }
-}
-
-
-
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModelView.java b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
deleted file mode 100644
index 4b7836b..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.modelviewer;
-
-import android.renderscript.Matrix4f;
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.ScaleGestureDetector;
-import android.util.Log;
-
-public class SimpleModelView extends RSSurfaceView implements SensorEventListener {
-
-    private RenderScriptGL mRS;
-    private SimpleModelRS mRender;
-
-    private ScaleGestureDetector mScaleDetector;
-
-    private SensorManager mSensorManager;
-    private Sensor mRotationVectorSensor;
-    private final float[] mRotationMatrix = new float[16];
-
-    private static final int INVALID_POINTER_ID = -1;
-    private int mActivePointerId = INVALID_POINTER_ID;
-    private boolean mUseSensor = false;
-    private Matrix4f mIdentityMatrix = new Matrix4f();
-
-    public SimpleModelView(Context context) {
-        super(context);
-        ensureRenderScript();
-        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
-        // Get an instance of the SensorManager
-        mSensorManager = (SensorManager)getContext().getSystemService(Context.SENSOR_SERVICE);
-        // find the rotation-vector sensor
-        mRotationVectorSensor = mSensorManager.getDefaultSensor(
-                Sensor.TYPE_ROTATION_VECTOR);
-        mIdentityMatrix.loadIdentity();
-    }
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRender = new SimpleModelRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    public void resume() {
-        mSensorManager.registerListener(this, mRotationVectorSensor, 10000);
-    }
-
-    @Override
-    public void pause() {
-        mSensorManager.unregisterListener(this);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        mRender.surfaceChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    public void loadA3DFile(String path) {
-        mRender.loadA3DFile(path);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mScaleDetector.onTouchEvent(ev);
-
-        boolean ret = false;
-        float x = ev.getX();
-        float y = ev.getY();
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN: {
-            mRender.onActionDown(x, y);
-            mActivePointerId = ev.getPointerId(0);
-            ret = true;
-            break;
-        }
-        case MotionEvent.ACTION_MOVE: {
-            if (!mScaleDetector.isInProgress()) {
-                mRender.onActionMove(x, y);
-            }
-            mRender.onActionDown(x, y);
-            ret = true;
-            break;
-        }
-
-        case MotionEvent.ACTION_UP: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_CANCEL: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_POINTER_UP: {
-            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-            final int pointerId = ev.getPointerId(pointerIndex);
-            if (pointerId == mActivePointerId) {
-                // This was our active pointer going up. Choose a new
-                // active pointer and adjust accordingly.
-                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-                x = ev.getX(newPointerIndex);
-                y = ev.getY(newPointerIndex);
-                mRender.onActionDown(x, y);
-                mActivePointerId = ev.getPointerId(newPointerIndex);
-            }
-            break;
-        }
-        }
-
-        return ret;
-    }
-
-    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-        @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            mRender.onActionScale(detector.getScaleFactor());
-            return true;
-        }
-    }
-
-    public void onSensorChanged(SensorEvent event) {
-        // we received a sensor event. it is a good practice to check
-        // that we received the proper event
-        if (mUseSensor) {
-            if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
-                // convert the rotation-vector to a 4x4 matrix. the matrix
-                // is interpreted by Open GL as the inverse of the
-                // rotation-vector, which is what we want.
-                SensorManager.getRotationMatrixFromVector(
-                        mRotationMatrix , event.values);
-
-                if (mRender != null) {
-                    mRender.onPostureChanged(new Matrix4f(mRotationMatrix));
-                }
-            }
-        }
-    }
-
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-    }
-
-    public void toggleSensor() {
-        mUseSensor = !mUseSensor;
-        if (mUseSensor == false) {
-            mRender.onPostureChanged(mIdentityMatrix);
-        }
-    }
-}
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/scenegraph.rs b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/scenegraph.rs
deleted file mode 100644
index 5c5b1c9..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/scenegraph.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.modelviewer)
-
-#include "rs_graphics.rsh"
-#include "transform_def.rsh"
-
-rs_program_vertex gPVBackground;
-rs_program_fragment gPFBackground;
-
-rs_allocation gTGrid;
-rs_mesh gTestMesh;
-
-rs_program_store gPFSBackground;
-
-float gRotate;
-
-rs_font gItalic;
-rs_allocation gTextAlloc;
-
-rs_script gTransformRS;
-
-SgTransform *gGroup;
-SgTransform *gRobot1;
-int gRobot1Index;
-SgTransform *gRobot2;
-int gRobot2Index;
-
-SgTransform *gRootNode;
-
-void init() {
-    gRotate = 0.0f;
-}
-
-int root(void) {
-
-    gGroup->transforms[1].w += 0.5f;
-    gGroup->isDirty = 1;
-
-    SgTransform *robot1Ptr = gRobot1 + gRobot1Index;
-
-    robot1Ptr->transforms[1].w -= 1.5f;
-    robot1Ptr->isDirty = 1;
-
-    SgTransform *robot2Ptr = gRobot2 + gRobot2Index;
-    robot2Ptr->transforms[1].w += 2.5f;
-    robot2Ptr->isDirty = 1;
-
-    rsForEach(gTransformRS, gRootNode->children, gRootNode->children);
-
-    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgClearDepth(1.0f);
-
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindProgramStore(gPFSBackground);
-    rsgBindTexture(gPFBackground, 0, gTGrid);
-
-    rsgProgramVertexLoadModelMatrix(&robot1Ptr->globalMat);
-    rsgDrawMesh(gTestMesh);
-
-    rsgProgramVertexLoadModelMatrix(&robot2Ptr->globalMat);
-    rsgDrawMesh(gTestMesh);
-
-    //color(0.3f, 0.3f, 0.3f, 1.0f);
-    rsgDrawText("Renderscript transform test", 30, 695);
-
-    rsgBindFont(gItalic);
-    rsgDrawText(gTextAlloc, 30, 730);
-
-    return 10;
-}
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs
deleted file mode 100644
index d3dd5b9..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/simplemodel.rs
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.modelviewer)
-
-#include "rs_graphics.rsh"
-
-rs_program_vertex gPVBackground;
-rs_program_fragment gPFBackground;
-
-rs_allocation gTGrid;
-
-rs_program_store gPFSBackground;
-
-rs_font gItalic;
-rs_allocation gTextAlloc;
-
-rs_matrix4x4 gPostureMatrix;
-
-typedef struct MeshInfo {
-    rs_mesh mMesh;
-    int mNumIndexSets;
-    float3 bBoxMin;
-    float3 bBoxMax;
-} MeshInfo_t;
-
-MeshInfo_t *gMeshes;
-
-static float3 gLookAt;
-
-static float gRotateX;
-static float gRotateY;
-static float gZoom;
-
-static float gLastX;
-static float gLastY;
-
-static float3 toFloat3(float x, float y, float z) {
-    float3 f;
-    f.x = x;
-    f.y = y;
-    f.z = z;
-    return f;
-}
-
-void onActionDown(float x, float y) {
-    gLastX = x;
-    gLastY = y;
-}
-
-void onActionScale(float scale) {
-
-    gZoom *= 1.0f / scale;
-    gZoom = max(0.1f, min(gZoom, 500.0f));
-}
-
-void onActionMove(float x, float y) {
-    float dx = gLastX - x;
-    float dy = gLastY - y;
-
-    if (fabs(dy) <= 2.0f) {
-        dy = 0.0f;
-    }
-    if (fabs(dx) <= 2.0f) {
-        dx = 0.0f;
-    }
-
-    gRotateY -= dx;
-    if (gRotateY > 360) {
-        gRotateY -= 360;
-    }
-    if (gRotateY < 0) {
-        gRotateY += 360;
-    }
-
-    gRotateX -= dy;
-    gRotateX = min(gRotateX, 80.0f);
-    gRotateX = max(gRotateX, -80.0f);
-
-    gLastX = x;
-    gLastY = y;
-}
-
-void init() {
-    gRotateX = 0.0f;
-    gRotateY = 0.0f;
-    gZoom = 50.0f;
-    gLookAt = 0.0f;
-    rsMatrixLoadIdentity(&gPostureMatrix);
-}
-
-void updateMeshInfo() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    float minX, minY, minZ, maxX, maxY, maxZ;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgMeshComputeBoundingBox(info->mMesh,
-                                  &minX, &minY, &minZ,
-                                  &maxX, &maxY, &maxZ);
-        info->bBoxMin = toFloat3(minX, minY, minZ);
-        info->bBoxMax = toFloat3(maxX, maxY, maxZ);
-        gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f;
-    }
-    gLookAt = gLookAt / (float)size;
-}
-
-static void renderAllMeshes() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgDrawMesh(info->mMesh);
-    }
-}
-
-void drawDescription() {
-    uint height = rsgGetHeight();
-    int left = 0, right = 0, top = 0, bottom = 0;
-
-    rsgBindFont(gItalic);
-
-    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
-    rsgDrawText(gTextAlloc, 2 -left, height - 2 + bottom);
-}
-
-int root(void) {
-
-    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgClearDepth(1.0f);
-
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindProgramStore(gPFSBackground);
-    rsgBindTexture(gPFBackground, 0, gTGrid);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    // Position our models on the screen
-    rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
-    rsMatrixMultiply(&matrix, &gPostureMatrix);
-    rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    renderAllMeshes();
-
-    drawDescription();
-
-    return 0;
-}
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/transform.rs b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/transform.rs
deleted file mode 100644
index 85c0630..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/transform.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.modelviewer)
-
-#include "transform_def.rsh"
-
-rs_script transformScript;
-
-typedef struct {
-    int changed;
-    rs_matrix4x4 *mat;
-} ParentData;
-
-static void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
-    rs_matrix4x4 temp;
-
-    switch (type) {
-    case TRANSFORM_TRANSLATE:
-        rsMatrixLoadTranslate(&temp, data.x, data.y, data.z);
-        break;
-    case TRANSFORM_ROTATE:
-        rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z);
-        break;
-    case TRANSFORM_SCALE:
-        rsMatrixLoadScale(&temp, data.x, data.y, data.z);
-        break;
-    }
-    rsMatrixMultiply(mat, &temp);
-}
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
-
-    SgTransform *data = (SgTransform *)v_out;
-    const ParentData *parent = (const ParentData *)usrData;
-
-    //rsDebug("Transform data", (int)data);
-    //rsDebug("Entering parent", (int)parent);
-
-    rs_matrix4x4 *localMat = &data->localMat;
-    rs_matrix4x4 *globalMat = &data->globalMat;
-
-    ParentData toChild;
-    toChild.changed = 0;
-    toChild.mat = globalMat;
-
-    //rsDebug("Transform is dirty", data->isDirty);
-
-    // Refresh matrices if dirty
-    if (data->isDirty) {
-        data->isDirty = 0;
-        toChild.changed = 1;
-
-        // Reset our local matrix
-        rsMatrixLoadIdentity(localMat);
-
-        for (int i = 0; i < 16; i ++) {
-            if (data->transformTypes[i] == TRANSFORM_NONE) {
-                break;
-            }
-            //rsDebug("Transform adding transformation", transformTypes[i]);
-            appendTransformation(data->transformTypes[i], data->transforms[i], localMat);
-        }
-    }
-
-    //rsDebug("Transform checking parent", (int)0);
-
-    if (parent) {
-        if (parent->changed) {
-            toChild.changed = 1;
-
-            rsMatrixLoad(globalMat, parent->mat);
-            rsMatrixMultiply(globalMat, localMat);
-        }
-    } else {
-        rsMatrixLoad(globalMat, localMat);
-    }
-
-    //rsDebug("Transform calling self with child ", (int)data->children.p);
-    if (data->children.p) {
-        rsForEach(transformScript, data->children, data->children, (void*)&toChild, sizeof(toChild));
-    }
-}
diff --git a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/transform_def.rsh b/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/transform_def.rsh
deleted file mode 100644
index 24a36c1..0000000
--- a/tests/RenderScriptTests/ModelViewer/src/com/android/modelviewer/transform_def.rsh
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.modelviewer)
-
-#define TRANSFORM_NONE 0
-#define TRANSFORM_TRANSLATE 1
-#define TRANSFORM_ROTATE 2
-#define TRANSFORM_SCALE 3
-
-typedef struct __attribute__((packed, aligned(4))) SgTransform {
-    rs_matrix4x4 globalMat;
-    rs_matrix4x4 localMat;
-
-    float4 transforms[16];
-    int transformTypes[16];
-
-    int isDirty;
-
-    rs_allocation children;
-
-} SgTransform;
diff --git a/tests/RenderScriptTests/PerfTest/Android.mk b/tests/RenderScriptTests/PerfTest/Android.mk
deleted file mode 100644
index e9ee771..0000000
--- a/tests/RenderScriptTests/PerfTest/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
-
-LOCAL_SDK_VERSION := 17
-
-LOCAL_PACKAGE_NAME := PerfTest
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/PerfTest/AndroidManifest.xml b/tests/RenderScriptTests/PerfTest/AndroidManifest.xml
deleted file mode 100644
index cc60396..0000000
--- a/tests/RenderScriptTests/PerfTest/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.perftest">
-    
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    
-    <uses-sdk android:minSdkVersion="11" />
-    <application android:label="PerfTest"
-      android:icon="@drawable/test_pattern">
-        <uses-library android:name="android.test.runner" />
-        <activity android:name="RsBench"
-                  android:label="RsBenchmark">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-      </application>
-
-      <instrumentation android:name=".RsPerfTestRunner"
-        android:targetPackage="com.android.perftest"
-        android:label="Test runner for RsBench tests"
-      />
-</manifest>
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
deleted file mode 100644
index b631e1e..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
deleted file mode 100644
index 8e34714..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
deleted file mode 100644
index 3a5c970..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
deleted file mode 100644
index f9d6172..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
deleted file mode 100644
index 3cd3775..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
deleted file mode 100644
index 2f2f10e..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
deleted file mode 100644
index b61f6a3..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
deleted file mode 100644
index e7d1455..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
deleted file mode 100644
index 1e08f3b..0000000
--- a/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml
deleted file mode 100644
index 59a251d..0000000
--- a/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/benchmark_all"
-          android:title="@string/benchmark_all" />
-    <item android:id="@+id/benchmark_one"
-          android:title="@string/benchmark_one" />
-    <item android:id="@+id/debug_mode"
-          android:title="@string/debug_mode" />
-</menu>
-
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/multitexf.glsl b/tests/RenderScriptTests/PerfTest/res/raw/multitexf.glsl
deleted file mode 100644
index e492a47..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/multitexf.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col0 = texture2D(UNI_Tex0, t0).rgba;
-   lowp vec4 col1 = texture2D(UNI_Tex1, t0*4.0).rgba;
-   lowp vec4 col2 = texture2D(UNI_Tex2, t0).rgba;
-   col0.xyz = col0.xyz*col1.xyz*1.5;
-   col0.xyz = mix(col0.xyz, col2.xyz, col2.w);
-   col0.w = 0.5;
-   gl_FragColor = col0;
-}
-
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/shader2f.glsl b/tests/RenderScriptTests/PerfTest/res/raw/shader2f.glsl
deleted file mode 100644
index 5fc05f1..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/shader2f.glsl
+++ /dev/null
@@ -1,29 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(-varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = normalize(UNI_light0_Posision.xyz - varWorldPos);
-   vec3 light0R = -reflect(light0Vec, worldNorm);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
-   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
-   float light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
-
-   vec3 light1Vec = normalize(UNI_light1_Posision.xyz - varWorldPos);
-   vec3 light1R = reflect(light1Vec, worldNorm);
-   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
-   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
-   float light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor.xyz + light1_Diffuse * UNI_light1_DiffuseColor.xyz);
-   col.xyz += light0_Specular * UNI_light0_SpecularColor.xyz;
-   col.xyz += light1_Specular * UNI_light1_SpecularColor.xyz;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/shader2movev.glsl b/tests/RenderScriptTests/PerfTest/res/raw/shader2movev.glsl
deleted file mode 100644
index a2c807e..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/shader2movev.glsl
+++ /dev/null
@@ -1,21 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 objPos = ATTRIB_position;
-   vec3 oldPos = objPos.xyz;
-   objPos.xyz += 0.1*sin(objPos.xyz*2.0 + UNI_time);
-   objPos.xyz += 0.05*sin(objPos.xyz*4.0 + UNI_time*0.5);
-   objPos.xyz += 0.02*sin(objPos.xyz*7.0 + UNI_time*0.75);
-   vec4 worldPos = UNI_model * objPos;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * (ATTRIB_normal + oldPos - objPos.xyz);
-
-   varWorldPos = worldPos.xyz;
-   varWorldNormal = worldNorm;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/shader2v.glsl b/tests/RenderScriptTests/PerfTest/res/raw/shader2v.glsl
deleted file mode 100644
index e6885a3..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/shader2v.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 objPos = ATTRIB_position;
-   vec4 worldPos = UNI_model * objPos;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * ATTRIB_normal;
-
-   varWorldPos = worldPos.xyz;
-   varWorldNormal = worldNorm;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/shaderf.glsl b/tests/RenderScriptTests/PerfTest/res/raw/shaderf.glsl
deleted file mode 100644
index d56e203..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/shaderf.glsl
+++ /dev/null
@@ -1,16 +0,0 @@
-
-varying lowp float light0_Diffuse;
-varying lowp float light0_Specular;
-varying lowp float light1_Diffuse;
-varying lowp float light1_Specular;
-varying vec2 varTex0;
-
-void main() {
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor.xyz + light1_Diffuse * UNI_light1_DiffuseColor.xyz);
-   col.xyz += light0_Specular * UNI_light0_SpecularColor.xyz;
-   col.xyz += light1_Specular * UNI_light1_SpecularColor.xyz;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/shaderv.glsl b/tests/RenderScriptTests/PerfTest/res/raw/shaderv.glsl
deleted file mode 100644
index f7d01de..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/shaderv.glsl
+++ /dev/null
@@ -1,30 +0,0 @@
-varying float light0_Diffuse;
-varying float light0_Specular;
-varying float light1_Diffuse;
-varying float light1_Specular;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 worldPos = UNI_model * ATTRIB_position;
-   gl_Position = UNI_proj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * ATTRIB_normal;
-   vec3 V = normalize(-worldPos.xyz);
-
-   vec3 light0Vec = normalize(UNI_light0_Posision.xyz - worldPos.xyz);
-   vec3 light0R = -reflect(light0Vec, worldNorm);
-   light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
-   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
-   light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
-
-   vec3 light1Vec = normalize(UNI_light1_Posision.xyz - worldPos.xyz);
-   vec3 light1R = reflect(light1Vec, worldNorm);
-   light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
-   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
-   light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
-
-   gl_PointSize = 1.0;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/singletexf.glsl b/tests/RenderScriptTests/PerfTest/res/raw/singletexf.glsl
deleted file mode 100644
index 83dfc7f..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/singletexf.glsl
+++ /dev/null
@@ -1,8 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   lowp vec3 col0 = texture2D(UNI_Tex0, varTex0).rgb;
-   gl_FragColor.xyz = col0;
-   gl_FragColor.w = 0.5;
-}
-
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl b/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl
deleted file mode 100644
index 656961c..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/singletexfm.glsl
+++ /dev/null
@@ -1,8 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   lowp vec3 col0 = texture2D(UNI_Tex0, varTex0).rgb;
-   gl_FragColor.xyz = col0 * UNI_modulate.rgb;
-   gl_FragColor.w = UNI_modulate.a;
-}
-
diff --git a/tests/RenderScriptTests/PerfTest/res/raw/torus.a3d b/tests/RenderScriptTests/PerfTest/res/raw/torus.a3d
deleted file mode 100644
index 0322b01..0000000
--- a/tests/RenderScriptTests/PerfTest/res/raw/torus.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/values/strings.xml b/tests/RenderScriptTests/PerfTest/res/values/strings.xml
deleted file mode 100644
index ce9819e..0000000
--- a/tests/RenderScriptTests/PerfTest/res/values/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <skip />
-    <string name="benchmark_all">Benchmark All</string>
-    <string name="benchmark_one">Benchmark One</string>
-    <string name="debug_mode">Debug Mode</string>
-</resources>
-
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
deleted file mode 100644
index 41f664a..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.os.Environment;
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-
-import android.util.Log;
-
-
-public class FillTest implements RsBenchBaseTest{
-
-    private static final String TAG = "FillTest";
-    private RenderScriptGL mRS;
-    private Resources mRes;
-
-    // Custom shaders
-    private ProgramFragment mProgFragmentMultitex;
-    private ProgramFragment mProgFragmentSingletex;
-    private ProgramFragment mProgFragmentSingletexModulate;
-    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
-    int mBenchmarkDimX;
-    int mBenchmarkDimY;
-
-    private ScriptC_fill_test mFillScript;
-    ScriptField_TestScripts_s.Item[] mTests;
-    ScriptField_FillTestFragData_s mFragData;
-
-    private final String[] mNames = {
-        "Fill screen 10x singletexture",
-        "Fill screen 10x 3tex multitexture",
-        "Fill screen 10x blended singletexture",
-        "Fill screen 10x blended 3tex multitexture",
-        "Fill screen 3x modulate blended singletexture",
-        "Fill screen 1x modulate blended singletexture",
-    };
-
-    public FillTest() {
-        mOptionsARGB.inScaled = false;
-        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        mBenchmarkDimX = 1280;
-        mBenchmarkDimY = 720;
-    }
-
-    void addTest(int index, int testId, int blend, int quadCount) {
-        mTests[index] = new ScriptField_TestScripts_s.Item();
-        mTests[index].testScript = mFillScript;
-        mTests[index].testName = Allocation.createFromString(mRS,
-                                                             mNames[index],
-                                                             Allocation.USAGE_SCRIPT);
-        mTests[index].debugName = RsBenchRS.createZeroTerminatedAlloc(mRS,
-                                                                      mNames[index],
-                                                                      Allocation.USAGE_SCRIPT);
-
-        ScriptField_FillTestData_s.Item dataItem = new ScriptField_FillTestData_s.Item();
-        dataItem.testId = testId;
-        dataItem.blend = blend;
-        dataItem.quadCount = quadCount;
-        ScriptField_FillTestData_s testData = new ScriptField_FillTestData_s(mRS, 1);
-        testData.set(dataItem, 0, true);
-        mTests[index].testData = testData.getAllocation();
-    }
-
-    public boolean init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initCustomShaders();
-        initFillScript();
-        mTests = new ScriptField_TestScripts_s.Item[mNames.length];
-
-        int index = 0;
-
-        addTest(index++, 1 /*testId*/, 0 /*blend*/, 10 /*quadCount*/);
-        addTest(index++, 0 /*testId*/, 0 /*blend*/, 10 /*quadCount*/);
-        addTest(index++, 1 /*testId*/, 1 /*blend*/, 10 /*quadCount*/);
-        addTest(index++, 0 /*testId*/, 1 /*blend*/, 10 /*quadCount*/);
-        addTest(index++, 2 /*testId*/, 1 /*blend*/, 3 /*quadCount*/);
-        addTest(index++, 2 /*testId*/, 1 /*blend*/, 1 /*quadCount*/);
-
-        return true;
-    }
-
-    public ScriptField_TestScripts_s.Item[] getTests() {
-        return mTests;
-    }
-
-    public String[] getTestNames() {
-        return mNames;
-    }
-
-    private void initCustomShaders() {
-        ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.multitexf);
-        for (int texCount = 0; texCount < 3; texCount ++) {
-            pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        }
-        mProgFragmentMultitex = pfbCustom.create();
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.singletexf);
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        mProgFragmentSingletex = pfbCustom.create();
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.singletexfm);
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        mFragData = new ScriptField_FillTestFragData_s(mRS, 1);
-        pfbCustom.addConstant(mFragData.getType());
-        mProgFragmentSingletexModulate = pfbCustom.create();
-        mProgFragmentSingletexModulate.bindConstants(mFragData.getAllocation(), 0);
-    }
-
-    private Allocation loadTextureARGB(int id) {
-        Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
-        return Allocation.createFromBitmap(mRS, b,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    private Allocation loadTextureRGB(int id) {
-        return Allocation.createFromBitmapResource(mRS, mRes, id,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    void initFillScript() {
-        mFillScript = new ScriptC_fill_test(mRS, mRes, R.raw.fill_test);
-
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        ProgramVertexFixedFunction progVertex = pvb.create();
-        ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)progVertex).bindConstants(PVA);
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(mBenchmarkDimX, mBenchmarkDimY);
-        PVA.setProjection(proj);
-        mFillScript.set_gProgVertex(progVertex);
-
-        mFillScript.set_gProgFragmentTexture(mProgFragmentSingletex);
-        mFillScript.set_gProgFragmentTextureModulate(mProgFragmentSingletexModulate);
-        mFillScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
-        mFillScript.set_gProgStoreBlendNone(ProgramStore.BLEND_NONE_DEPTH_NONE(mRS));
-        mFillScript.set_gProgStoreBlendAlpha(ProgramStore.BLEND_ALPHA_DEPTH_NONE(mRS));
-
-        mFillScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
-        mFillScript.set_gLinearWrap(Sampler.WRAP_LINEAR(mRS));
-        mFillScript.set_gTexTorus(loadTextureRGB(R.drawable.torusmap));
-        mFillScript.set_gTexOpaque(loadTextureRGB(R.drawable.data));
-        mFillScript.set_gTexTransparent(loadTextureARGB(R.drawable.leaf));
-        mFillScript.set_gTexChecker(loadTextureRGB(R.drawable.checker));
-
-        mFillScript.bind_gFragData(mFragData);
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/MeshTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/MeshTest.java
deleted file mode 100644
index cdb4435..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/MeshTest.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.os.Environment;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.renderscript.*;
-import android.renderscript.Element.DataKind;
-import android.renderscript.Element.DataType;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Program.TextureType;
-import android.renderscript.RenderScript.RSMessageHandler;
-import android.renderscript.Mesh.Primitive;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramVertexFixedFunction;
-
-import android.util.Log;
-
-
-public class MeshTest implements RsBenchBaseTest{
-
-    private static final String TAG = "MeshTest";
-    private RenderScriptGL mRS;
-    private Resources mRes;
-
-    int mBenchmarkDimX;
-    int mBenchmarkDimY;
-
-    private Mesh m10by10Mesh;
-    private Mesh m100by100Mesh;
-    private Mesh mWbyHMesh;
-
-    private ScriptC_mesh_test mGeoScript;
-
-    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
-
-    ScriptField_TestScripts_s.Item[] mTests;
-
-    private final String[] mNames = {
-        "Full screen mesh 10 by 10",
-        "Full screen mesh 100 by 100",
-        "Full screen mesh W / 4 by H / 4"
-    };
-
-    public MeshTest() {
-        mBenchmarkDimX = 1280;
-        mBenchmarkDimY = 720;
-    }
-
-    void addTest(int index, int meshNum) {
-        mTests[index] = new ScriptField_TestScripts_s.Item();
-        mTests[index].testScript = mGeoScript;
-        mTests[index].testName = Allocation.createFromString(mRS,
-                                                             mNames[index],
-                                                             Allocation.USAGE_SCRIPT);
-        mTests[index].debugName = RsBenchRS.createZeroTerminatedAlloc(mRS,
-                                                                      mNames[index],
-                                                                      Allocation.USAGE_SCRIPT);
-
-        ScriptField_MeshTestData_s.Item dataItem = new ScriptField_MeshTestData_s.Item();
-        dataItem.meshNum = meshNum;
-        ScriptField_MeshTestData_s testData = new ScriptField_MeshTestData_s(mRS, 1);
-        testData.set(dataItem, 0, true);
-        mTests[index].testData = testData.getAllocation();
-    }
-
-    public boolean init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initGeoScript();
-        mTests = new ScriptField_TestScripts_s.Item[mNames.length];
-
-        int index = 0;
-        addTest(index++, 0 /*meshNum*/);
-        addTest(index++, 1 /*meshNum*/);
-        addTest(index++, 2 /*meshNum*/);
-
-        return true;
-    }
-
-    public ScriptField_TestScripts_s.Item[] getTests() {
-        return mTests;
-    }
-
-    public String[] getTestNames() {
-        return mNames;
-    }
-
-    private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) {
-
-        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
-                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
-
-        for (int y = 0; y <= hResolution; y++) {
-            final float normalizedY = (float)y / hResolution;
-            final float yOffset = (normalizedY - 0.5f) * height;
-            for (int x = 0; x <= wResolution; x++) {
-                float normalizedX = (float)x / wResolution;
-                float xOffset = (normalizedX - 0.5f) * width;
-                tmb.setTexture((float)x % 2, (float)y % 2);
-                tmb.addVertex(xOffset, yOffset);
-             }
-        }
-
-        for (int y = 0; y < hResolution; y++) {
-            final int curY = y * (wResolution + 1);
-            final int belowY = (y + 1) * (wResolution + 1);
-            for (int x = 0; x < wResolution; x++) {
-                int curV = curY + x;
-                int belowV = belowY + x;
-                tmb.addTriangle(curV, belowV, curV + 1);
-                tmb.addTriangle(belowV, belowV + 1, curV + 1);
-            }
-        }
-
-        return tmb.create(true);
-    }
-
-    private Allocation loadTextureRGB(int id) {
-        return Allocation.createFromBitmapResource(mRS, mRes, id,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    void initGeoScript() {
-        mGeoScript = new ScriptC_mesh_test(mRS, mRes, R.raw.mesh_test);
-
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        ProgramVertexFixedFunction progVertex = pvb.create();
-        ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)progVertex).bindConstants(PVA);
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(mBenchmarkDimX, mBenchmarkDimY);
-        PVA.setProjection(proj);
-
-        mGeoScript.set_gProgVertex(progVertex);
-        ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                              ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mGeoScript.set_gProgFragmentTexture(texBuilder.create());
-        mGeoScript.set_gProgStoreBlendNone(ProgramStore.BLEND_NONE_DEPTH_NONE(mRS));
-
-        mGeoScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
-        mGeoScript.set_gTexOpaque(loadTextureRGB(R.drawable.data));
-
-        m10by10Mesh = getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, 10, 10);
-        m100by100Mesh = getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, 100, 100);
-        mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4);
-
-        mGeoScript.set_g10by10Mesh(m10by10Mesh);
-        mGeoScript.set_g100by100Mesh(m100by100Mesh);
-        mGeoScript.set_gWbyHMesh(mWbyHMesh);
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java
deleted file mode 100644
index 0dceafe..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.widget.Toast;
-
-import java.lang.Runtime;
-
-public class RsBench extends Activity {
-    private final String TAG = "RsBench";
-    public RsBenchView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        int iterations = 0;
-        Intent intent = getIntent();
-        Uri uri = intent.getData();
-        if (uri != null) {
-            // when lauched from instrumentation
-            String scheme = uri.getScheme();
-            if ("iterations".equals(scheme)) {
-                iterations = Integer.parseInt(uri.getSchemeSpecificPart());
-            }
-        }
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new RsBenchView(this);
-        setContentView(mView);
-        mView.setLoops(iterations);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity loses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity loses focus
-        super.onPause();
-        mView.pause();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.loader_menu, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        // Handle item selection
-        switch (item.getItemId()) {
-            case R.id.benchmark_all:
-                mView.setBenchmarkMode(-1);
-                mView.suspendRendering(false);
-                return true;
-            case R.id.benchmark_one:
-                mView.suspendRendering(true);
-                AlertDialog.Builder builder = new AlertDialog.Builder(this);
-                builder.setTitle("Pick a Test");
-                builder.setItems(mView.getTestNames(),
-                                 new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int item) {
-                        Toast.makeText(getApplicationContext(),
-                                       "Starting to benchmark: " + mView.getTestNames()[item],
-                                       Toast.LENGTH_SHORT).show();
-                        mView.setBenchmarkMode(item);
-                        mView.suspendRendering(false);
-                    }
-                });
-                builder.show();
-                return true;
-            case R.id.debug_mode:
-                mView.suspendRendering(true);
-                AlertDialog.Builder debugBuilder = new AlertDialog.Builder(this);
-                debugBuilder.setTitle("Pick a Test");
-                debugBuilder.setItems(mView.getTestNames(),
-                                 new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int item) {
-                        Toast.makeText(getApplicationContext(),
-                                       "Switching to: " + mView.getTestNames()[item],
-                                       Toast.LENGTH_SHORT).show();
-                        mView.setDebugMode(item);
-                        mView.suspendRendering(false);
-                    }
-                });
-                debugBuilder.show();
-                return true;
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
deleted file mode 100644
index 4ac7dd5..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2010-2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import java.io.Writer;
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-
-import android.os.Environment;
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.Element.DataKind;
-import android.renderscript.Element.DataType;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Program.TextureType;
-import android.renderscript.RenderScript.RSMessageHandler;
-import android.renderscript.Sampler.Value;
-import android.renderscript.Mesh.Primitive;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramVertexFixedFunction;
-
-import android.util.Log;
-
-
-public class RsBenchRS {
-
-    private static final String TAG = "RsBenchRS";
-    int mWidth;
-    int mHeight;
-    int mLoops;
-    int mCurrentLoop;
-
-    int mBenchmarkDimX;
-    int mBenchmarkDimY;
-
-    public RsBenchRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res, int width, int height, int loops) {
-        mRS = rs;
-        mRes = res;
-        mWidth = width;
-        mHeight = height;
-        mMode = 0;
-        mLoops = loops;
-        mCurrentLoop = 0;
-        mBenchmarkDimX = 1280;
-        mBenchmarkDimY = 720;
-        initRS();
-    }
-
-    private boolean stopTest = false;
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-
-    private ProgramStore mProgStoreBlendNone;
-    private ProgramStore mProgStoreBlendAlpha;
-
-    private ProgramFragment mProgFragmentTexture;
-    private ProgramFragment mProgFragmentColor;
-
-    private ProgramVertex mProgVertex;
-    private ProgramVertexFixedFunction.Constants mPVA;
-    private ProgramVertexFixedFunction.Constants mPvProjectionAlloc;
-
-    private ScriptC_rsbench mScript;
-
-    ScriptField_TestScripts_s.Item[] mIndividualTests;
-
-    int mMode;
-
-    String[] mTestNames;
-    float[] mLocalTestResults;
-
-    static Allocation createZeroTerminatedAlloc(RenderScript rs,
-                                                String str,
-                                                int usage) {
-        byte[] allocArray = null;
-        try {
-            allocArray = str.getBytes("UTF-8");
-            byte[] allocArrayZero = new byte[allocArray.length + 1];
-            System.arraycopy(allocArray, 0, allocArrayZero, 0, allocArray.length);
-            allocArrayZero[allocArrayZero.length - 1] = '\0';
-            Allocation alloc = Allocation.createSized(rs, Element.U8(rs),
-                                                      allocArrayZero.length, usage);
-            alloc.copyFrom(allocArrayZero);
-            return alloc;
-        }
-        catch (Exception e) {
-            throw new RSRuntimeException("Could not convert string to utf-8.");
-        }
-
-    }
-
-    void appendTests(RsBenchBaseTest testSet) {
-        ScriptField_TestScripts_s.Item[] newTests = testSet.getTests();
-        if (mIndividualTests != null) {
-            ScriptField_TestScripts_s.Item[] combined;
-            combined = new ScriptField_TestScripts_s.Item[newTests.length + mIndividualTests.length];
-            System.arraycopy(mIndividualTests, 0, combined, 0, mIndividualTests.length);
-            System.arraycopy(newTests, 0, combined, mIndividualTests.length, newTests.length);
-            mIndividualTests = combined;
-        } else {
-            mIndividualTests = newTests;
-        }
-
-        String[] newNames = testSet.getTestNames();
-        if (mTestNames != null) {
-            String[] combinedNames;
-            combinedNames = new String[newNames.length + mTestNames.length];
-            System.arraycopy(mTestNames, 0, combinedNames, 0, mTestNames.length);
-            System.arraycopy(newNames, 0, combinedNames, mTestNames.length, newNames.length);
-            mTestNames = combinedNames;
-        } else {
-            mTestNames = newNames;
-        }
-    }
-
-    void createTestAllocation() {
-        int numTests = mIndividualTests.length;
-        mLocalTestResults = new float[numTests];
-        ScriptField_TestScripts_s allTests;
-        allTests = new ScriptField_TestScripts_s(mRS, numTests);
-        for (int i = 0; i < numTests; i ++) {
-            allTests.set(mIndividualTests[i], i, false);
-        }
-        allTests.copyAll();
-        mScript.bind_gTestScripts(allTests);
-    }
-
-    private void saveTestResults() {
-        String state = Environment.getExternalStorageState();
-        if (!Environment.MEDIA_MOUNTED.equals(state)) {
-            Log.v(TAG, "sdcard is read only");
-            return;
-        }
-        File sdCard = Environment.getExternalStorageDirectory();
-        if (!sdCard.canWrite()) {
-            Log.v(TAG, "ssdcard is read only");
-            return;
-        }
-
-        File resultFile = new File(sdCard, "rsbench_result" + mCurrentLoop + ".csv");
-        resultFile.setWritable(true, false);
-
-        try {
-            BufferedWriter results = new BufferedWriter(new FileWriter(resultFile));
-            for (int i = 0; i < mLocalTestResults.length; i ++) {
-                results.write(mTestNames[i] + ", " + mLocalTestResults[i] + ",\n");
-            }
-            results.close();
-            Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
-        } catch (IOException e) {
-            Log.v(TAG, "Unable to write result file " + e.getMessage());
-        }
-    }
-
-    /**
-     * Create a message handler to handle message sent from the script
-     */
-    protected RSMessageHandler mRsMessage = new RSMessageHandler() {
-        public void run() {
-            if (mID == mScript.get_RS_MSG_RESULTS_READY()) {
-                for (int i = 0; i < mLocalTestResults.length; i ++) {
-                    mLocalTestResults[i] = Float.intBitsToFloat(mData[i]);
-                }
-                saveTestResults();
-                if (mLoops > 0) {
-                    mCurrentLoop ++;
-                    mCurrentLoop = mCurrentLoop % mLoops;
-                }
-                return;
-
-            } else if (mID == mScript.get_RS_MSG_TEST_DONE()) {
-                synchronized(this) {
-                    stopTest = true;
-                    this.notifyAll();
-                }
-                return;
-            } else {
-                Log.v(TAG, "Perf test got unexpected message");
-                return;
-            }
-        }
-    };
-
-    /**
-     * Wait for message from the script
-     */
-    public boolean testIsFinished() {
-        synchronized(this) {
-            while (true) {
-                if (stopTest) {
-                    return true;
-                } else {
-                    try {
-                        this.wait(60*1000);
-                    } catch (InterruptedException e) {
-                        e.printStackTrace();
-                    }
-                }
-            }
-        }
-    }
-
-    private void initProgramFragment() {
-
-        ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                              ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mProgFragmentTexture = texBuilder.create();
-        mProgFragmentTexture.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
-
-        ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        colBuilder.setVaryingColor(false);
-        mProgFragmentColor = colBuilder.create();
-
-        mScript.set_gProgFragmentTexture(mProgFragmentTexture);
-    }
-
-    private void initProgramVertex() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mProgVertex = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mProgVertex).bindConstants(mPVA);
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(mBenchmarkDimX, mBenchmarkDimY);
-        mPVA.setProjection(proj);
-
-        mScript.set_gProgVertex(mProgVertex);
-    }
-
-    private int strlen(byte[] array) {
-        int count = 0;
-        while(count < array.length && array[count] != 0) {
-            count ++;
-        }
-        return count;
-    }
-
-    public void setDebugMode(int num) {
-        mScript.invoke_setDebugMode(num);
-    }
-
-    public void setBenchmarkMode(int benchNum) {
-        mScript.invoke_setBenchmarkMode(benchNum);
-    }
-
-    public void pause(boolean pause) {
-        mScript.set_gPauseRendering(pause);
-    }
-
-    private void initRS() {
-
-        mScript = new ScriptC_rsbench(mRS, mRes, R.raw.rsbench);
-        mRS.bindRootScript(mScript);
-
-        mRS.setMessageHandler(mRsMessage);
-
-        mScript.set_gMaxLoops(mLoops);
-
-        initProgramVertex();
-        initProgramFragment();
-        mScript.set_gFontSerif(Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8));
-
-        Type.Builder b = new Type.Builder(mRS, Element.RGBA_8888(mRS));
-        b.setX(mBenchmarkDimX).setY(mBenchmarkDimY);
-        Allocation offscreen = Allocation.createTyped(mRS,
-                                                      b.create(),
-                                                      Allocation.USAGE_GRAPHICS_TEXTURE |
-                                                      Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gRenderBufferColor(offscreen);
-
-        b = new Type.Builder(mRS,
-                             Element.createPixel(mRS, DataType.UNSIGNED_16,
-                             DataKind.PIXEL_DEPTH));
-        b.setX(mBenchmarkDimX).setY(mBenchmarkDimY);
-        offscreen = Allocation.createTyped(mRS,
-                                           b.create(),
-                                           Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gRenderBufferDepth(offscreen);
-        mScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
-
-        RsBenchBaseTest test = new TextTest();
-        if (test.init(mRS, mRes)) {
-            appendTests(test);
-        }
-        test = new FillTest();
-        if (test.init(mRS, mRes)) {
-            appendTests(test);
-        }
-        test = new MeshTest();
-        if (test.init(mRS, mRes)) {
-            appendTests(test);
-        }
-        test = new TorusTest();
-        if (test.init(mRS, mRes)) {
-            appendTests(test);
-        }
-        test = new UiTest();
-        if (test.init(mRS, mRes)) {
-            appendTests(test);
-        }
-        createTestAllocation();
-
-        mScript.set_gLoadComplete(true);
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchTest.java
deleted file mode 100644
index 199200b..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.TouchUtils;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-
-/**
- * To run the test, please use command
- *
- * adb shell am instrument -w com.android.perftest/.RsPerfTestRunner
- *
- */
-public class RsBenchTest extends ActivityInstrumentationTestCase2<RsBench> {
-    private String TAG = "RsBenchTest";
-    private int iterations = 0;
-    private RsBench mAct;
-
-    public RsBenchTest() {
-        super(RsBench.class);
-    }
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        Instrumentation mInst = getInstrumentation();
-        RsPerfTestRunner mRunner = (RsPerfTestRunner) getInstrumentation();
-        iterations = mRunner.iterations;
-        Log.v(TAG, "Run benchmark for " + iterations + " iterations.");
-
-        Uri data = Uri.fromParts("iterations", Integer.toString(iterations), null);
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.setClassName("com.android.perftest", "com.android.perftest.RsBench");
-        intent.setData(data);
-        mAct = (RsBench) mInst.startActivitySync(intent);
-        mInst.waitForIdleSync();
-
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        mAct.finish();
-        super.tearDown();
-    }
-
-    /**
-     * Run tests and wait until the test has been run for iterations.
-     */
-    @LargeTest
-    public void testRsBench() {
-        if (mAct.mView.testIsFinished()) {
-            return;
-        } else {
-            fail("test didn't stop correctly");
-        }
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java
deleted file mode 100644
index 124071e..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-import android.renderscript.RenderScript.RSMessageHandler;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-public class RsBenchView extends RSSurfaceView {
-
-    public RsBenchView(Context context) {
-        super(context);
-    }
-
-    private RenderScriptGL mRS;
-    private RsBenchRS mRender;
-    private int mLoops = 0;
-
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRS.setSurface(holder, w, h);
-            mRender = new RsBenchRS();
-            Log.v("RsBenchView", "mLoops = " + mLoops);
-            mRender.init(mRS, getResources(), w, h, mLoops);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    /**
-     * Set the total number of loops the benchmark tests will run
-     * before the test results are collected.
-     */
-    public void setLoops(int iterations) {
-        if (iterations > 0) {
-            mLoops = iterations;
-        }
-    }
-
-    /**
-     * Wait for message from the script
-     */
-    public boolean testIsFinished() {
-        return mRender.testIsFinished();
-    }
-
-    void setBenchmarkMode(int benchNum) {
-        mRender.setBenchmarkMode(benchNum);
-    }
-
-    void suspendRendering(boolean pause) {
-        mRender.pause(pause);
-    }
-
-    void setDebugMode(int num) {
-        mRender.setDebugMode(num);
-    }
-
-    String[] getTestNames() {
-        return mRender.mTestNames;
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsPerfTestRunner.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsPerfTestRunner.java
deleted file mode 100644
index 031af6a..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsPerfTestRunner.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-//import com.android.perftest.RsBenchTest;
-
-import android.os.Bundle;
-import android.test.InstrumentationTestRunner;
-import android.test.InstrumentationTestSuite;
-
-import junit.framework.TestSuite;
-
-/**
- * Run the RenderScript Performance Test
- * adb shell am instrument -w com.android.perftest/.RsPerfTestRunner
- *
- * with specified iterations:
- * adb shell am instrument -e iterations <n> -w com.android.perftest/.RsPerfTestRunner
- *
- */
-public class RsPerfTestRunner extends InstrumentationTestRunner {
-    public int iterations = 10;
-
-    @Override
-    public TestSuite getAllTests() {
-        TestSuite suite = new InstrumentationTestSuite(this);
-        suite.addTestSuite(RsBenchTest.class);
-        return suite;
-    }
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        String strValue = (String)icicle.get("iterations");
-        if (strValue != null) {
-            int intValue = Integer.parseInt(strValue);
-            if (iterations > 0) {
-                iterations = intValue;
-            }
-        }
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TextTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TextTest.java
deleted file mode 100644
index 3ca2792..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TextTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.os.Environment;
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.DisplayMetrics;
-
-import android.util.Log;
-
-
-public class TextTest implements RsBenchBaseTest{
-
-    private static final String TAG = "TextTest";
-    private RenderScriptGL mRS;
-    private Resources mRes;
-
-    private ScriptC_text_test mTextScript;
-    ScriptField_TestScripts_s.Item[] mTests;
-
-    private final String[] mNames = {
-        "Fill screen with text 1 time",
-        "Fill screen with text 3 times",
-        "Fill screen with text 5 times"
-    };
-
-    public TextTest() {
-    }
-
-    void addTest(int index, int fillNum) {
-        mTests[index] = new ScriptField_TestScripts_s.Item();
-        mTests[index].testScript = mTextScript;
-        mTests[index].testName = Allocation.createFromString(mRS,
-                                                             mNames[index],
-                                                             Allocation.USAGE_SCRIPT);
-        mTests[index].debugName = RsBenchRS.createZeroTerminatedAlloc(mRS,
-                                                                     mNames[index],
-                                                                     Allocation.USAGE_SCRIPT);
-
-        ScriptField_TextTestData_s.Item dataItem = new ScriptField_TextTestData_s.Item();
-        dataItem.fillNum = fillNum;
-        ScriptField_TextTestData_s testData = new ScriptField_TextTestData_s(mRS, 1);
-        testData.set(dataItem, 0, true);
-        mTests[index].testData = testData.getAllocation();
-    }
-
-    public boolean init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initTextScript();
-        mTests = new ScriptField_TestScripts_s.Item[mNames.length];
-
-        int index = 0;
-        addTest(index++, 1 /*fillNum*/);
-        addTest(index++, 3 /*fillNum*/);
-        addTest(index++, 5 /*fillNum*/);
-
-        return true;
-    }
-
-    public ScriptField_TestScripts_s.Item[] getTests() {
-        return mTests;
-    }
-
-    public String[] getTestNames() {
-        return mNames;
-    }
-
-    void initTextScript() {
-        DisplayMetrics metrics = mRes.getDisplayMetrics();
-
-        mTextScript = new ScriptC_text_test(mRS, mRes, R.raw.text_test);
-        mTextScript.set_gFontSans(Font.create(mRS, mRes, "sans-serif",
-                                              Font.Style.NORMAL, 8.0f / metrics.density));
-        mTextScript.set_gFontSerif(Font.create(mRS, mRes, "serif",
-                                               Font.Style.NORMAL, 8.0f / metrics.density));
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TorusTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TorusTest.java
deleted file mode 100644
index 5c9ecd5..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TorusTest.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.os.Environment;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.renderscript.*;
-import android.renderscript.Element.DataKind;
-import android.renderscript.Element.DataType;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Program.TextureType;
-import android.renderscript.RenderScript.RSMessageHandler;
-import android.renderscript.Mesh.Primitive;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramVertexFixedFunction;
-
-import android.util.Log;
-
-
-public class TorusTest implements RsBenchBaseTest{
-
-    private static final String TAG = "TorusTest";
-    private RenderScriptGL mRS;
-    private Resources mRes;
-
-    private ProgramStore mProgStoreBlendNoneDepth;
-    private ProgramStore mProgStoreBlendNone;
-    private ProgramStore mProgStoreBlendAlpha;
-
-    private ProgramFragment mProgFragmentTexture;
-    private ProgramFragment mProgFragmentColor;
-
-    private ProgramVertex mProgVertex;
-    private ProgramVertexFixedFunction.Constants mPVA;
-    private ProgramVertexFixedFunction.Constants mPvProjectionAlloc;
-
-    // Custom shaders
-    private ProgramVertex mProgVertexCustom;
-    private ProgramFragment mProgFragmentCustom;
-    private ProgramFragment mProgFragmentMultitex;
-    private ProgramVertex mProgVertexPixelLight;
-    private ProgramVertex mProgVertexPixelLightMove;
-    private ProgramFragment mProgFragmentPixelLight;
-    private ScriptField_VertexShaderConstants_s mVSConst;
-    private ScriptField_FragentShaderConstants_s mFSConst;
-    private ScriptField_VertexShaderConstants3_s mVSConstPixel;
-    private ScriptField_FragentShaderConstants3_s mFSConstPixel;
-
-    private Allocation mTexTorus;
-    private Mesh mTorus;
-
-    private ScriptC_torus_test mTorusScript;
-
-    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
-
-    ScriptField_TestScripts_s.Item[] mTests;
-
-    private final String[] mNames = {
-        "Geo test 25.6k flat color",
-        "Geo test 51.2k flat color",
-        "Geo test 204.8k small tries flat color",
-        "Geo test 25.6k single texture",
-        "Geo test 51.2k single texture",
-        "Geo test 204.8k small tries single texture",
-        "Geo test 25.6k geo heavy vertex",
-        "Geo test 51.2k geo heavy vertex",
-        "Geo test 204.8k geo raster load heavy vertex",
-        "Geo test 25.6k heavy fragment",
-        "Geo test 51.2k heavy fragment",
-        "Geo test 204.8k small tries heavy fragment",
-        "Geo test 25.6k heavy fragment heavy vertex",
-        "Geo test 51.2k heavy fragment heavy vertex",
-        "Geo test 204.8k small tries heavy fragment heavy vertex"
-    };
-
-    public TorusTest() {
-    }
-
-    void addTest(int index, int testId, int user1, int user2) {
-        mTests[index] = new ScriptField_TestScripts_s.Item();
-        mTests[index].testScript = mTorusScript;
-        mTests[index].testName = Allocation.createFromString(mRS,
-                                                             mNames[index],
-                                                             Allocation.USAGE_SCRIPT);
-        mTests[index].debugName = RsBenchRS.createZeroTerminatedAlloc(mRS,
-                                                                      mNames[index],
-                                                                      Allocation.USAGE_SCRIPT);
-
-        ScriptField_TorusTestData_s.Item dataItem = new ScriptField_TorusTestData_s.Item();
-        dataItem.testId = testId;
-        dataItem.user1 = user1;
-        dataItem.user2 = user2;
-        ScriptField_TorusTestData_s testData = new ScriptField_TorusTestData_s(mRS, 1);
-        testData.set(dataItem, 0, true);
-        mTests[index].testData = testData.getAllocation();
-    }
-
-    public boolean init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initCustomShaders();
-        loadImages();
-        initMesh();
-        initTorusScript();
-        mTests = new ScriptField_TestScripts_s.Item[mNames.length];
-
-        int index = 0;
-        addTest(index++, 0, 0 /*useTexture*/, 1 /*numMeshes*/);
-        addTest(index++, 0, 0 /*useTexture*/, 2 /*numMeshes*/);
-        addTest(index++, 0, 0 /*useTexture*/, 8 /*numMeshes*/);
-        addTest(index++, 0, 1 /*useTexture*/, 1 /*numMeshes*/);
-        addTest(index++, 0, 1 /*useTexture*/, 2 /*numMeshes*/);
-        addTest(index++, 0, 1 /*useTexture*/, 8 /*numMeshes*/);
-
-        // Secont test
-        addTest(index++, 1, 1 /*numMeshes*/, 0 /*unused*/);
-        addTest(index++, 1, 2 /*numMeshes*/, 0 /*unused*/);
-        addTest(index++, 1, 8 /*numMeshes*/, 0 /*unused*/);
-
-        // Third test
-        addTest(index++, 2, 1 /*numMeshes*/, 0 /*heavyVertex*/);
-        addTest(index++, 2, 2 /*numMeshes*/, 0 /*heavyVertex*/);
-        addTest(index++, 2, 8 /*numMeshes*/, 0 /*heavyVertex*/);
-        addTest(index++, 2, 1 /*numMeshes*/, 1 /*heavyVertex*/);
-        addTest(index++, 2, 2 /*numMeshes*/, 1 /*heavyVertex*/);
-        addTest(index++, 2, 8 /*numMeshes*/, 1 /*heavyVertex*/);
-
-        return true;
-    }
-
-    public ScriptField_TestScripts_s.Item[] getTests() {
-        return mTests;
-    }
-
-    public String[] getTestNames() {
-        return mNames;
-    }
-
-    private void initCustomShaders() {
-        mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1);
-        mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1);
-
-        mVSConstPixel = new ScriptField_VertexShaderConstants3_s(mRS, 1);
-        mFSConstPixel = new ScriptField_FragentShaderConstants3_s(mRS, 1);
-
-        // Initialize the shader builder
-        ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS);
-        // Specify the resource that contains the shader string
-        pvbCustom.setShader(mRes, R.raw.shaderv);
-        // Use a script field to specify the input layout
-        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
-        // Define the constant input layout
-        pvbCustom.addConstant(mVSConst.getAllocation().getType());
-        mProgVertexCustom = pvbCustom.create();
-        // Bind the source of constant data
-        mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0);
-
-        ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS);
-        // Specify the resource that contains the shader string
-        pfbCustom.setShader(mRes, R.raw.shaderf);
-        // Tell the builder how many textures we have
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        // Define the constant input layout
-        pfbCustom.addConstant(mFSConst.getAllocation().getType());
-        mProgFragmentCustom = pfbCustom.create();
-        // Bind the source of constant data
-        mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
-
-        pvbCustom = new ProgramVertex.Builder(mRS);
-        pvbCustom.setShader(mRes, R.raw.shader2v);
-        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
-        pvbCustom.addConstant(mVSConstPixel.getAllocation().getType());
-        mProgVertexPixelLight = pvbCustom.create();
-        mProgVertexPixelLight.bindConstants(mVSConstPixel.getAllocation(), 0);
-
-        pvbCustom = new ProgramVertex.Builder(mRS);
-        pvbCustom.setShader(mRes, R.raw.shader2movev);
-        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
-        pvbCustom.addConstant(mVSConstPixel.getAllocation().getType());
-        mProgVertexPixelLightMove = pvbCustom.create();
-        mProgVertexPixelLightMove.bindConstants(mVSConstPixel.getAllocation(), 0);
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.shader2f);
-        pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        pfbCustom.addConstant(mFSConstPixel.getAllocation().getType());
-        mProgFragmentPixelLight = pfbCustom.create();
-        mProgFragmentPixelLight.bindConstants(mFSConstPixel.getAllocation(), 0);
-
-        pfbCustom = new ProgramFragment.Builder(mRS);
-        pfbCustom.setShader(mRes, R.raw.multitexf);
-        for (int texCount = 0; texCount < 3; texCount ++) {
-            pfbCustom.addTexture(Program.TextureType.TEXTURE_2D);
-        }
-        mProgFragmentMultitex = pfbCustom.create();
-
-        ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        colBuilder.setVaryingColor(false);
-        mProgFragmentColor = colBuilder.create();
-
-        ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                              ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mProgFragmentTexture = texBuilder.create();
-
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mProgVertex = pvb.create();
-        ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)mProgVertex).bindConstants(PVA);
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(1280, 720);
-        PVA.setProjection(proj);
-    }
-
-    private Allocation loadTextureRGB(int id) {
-        return Allocation.createFromBitmapResource(mRS, mRes, id,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    private void loadImages() {
-        mTexTorus = loadTextureRGB(R.drawable.torusmap);
-    }
-
-    private void initMesh() {
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
-        FileA3D.IndexEntry entry = model.getIndexEntry(0);
-        if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
-            Log.e("rs", "could not load model");
-        } else {
-            mTorus = (Mesh)entry.getObject();
-        }
-    }
-
-    void initTorusScript() {
-        mTorusScript = new ScriptC_torus_test(mRS, mRes, R.raw.torus_test);
-        mTorusScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS));
-        mTorusScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS));
-        mTorusScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
-        mTorusScript.set_gTorusMesh(mTorus);
-        mTorusScript.set_gTexTorus(mTexTorus);
-        mTorusScript.set_gProgVertexCustom(mProgVertexCustom);
-        mTorusScript.set_gProgFragmentCustom(mProgFragmentCustom);
-        mTorusScript.set_gProgVertexPixelLight(mProgVertexPixelLight);
-        mTorusScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove);
-        mTorusScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight);
-        mTorusScript.bind_gVSConstPixel(mVSConstPixel);
-        mTorusScript.bind_gFSConstPixel(mFSConstPixel);
-        mTorusScript.bind_gVSConstants(mVSConst);
-        mTorusScript.bind_gFSConstants(mFSConst);
-        mTorusScript.set_gProgVertex(mProgVertex);
-        mTorusScript.set_gProgFragmentTexture(mProgFragmentTexture);
-        mTorusScript.set_gProgFragmentColor(mProgFragmentColor);
-        mTorusScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/UiTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/UiTest.java
deleted file mode 100644
index c8b58b2..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/UiTest.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.perftest;
-
-import android.os.Environment;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.renderscript.*;
-import android.renderscript.Element.DataKind;
-import android.renderscript.Element.DataType;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Program.TextureType;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.renderscript.ProgramStore.BlendSrcFunc;
-import android.renderscript.ProgramStore.BlendDstFunc;
-import android.renderscript.RenderScript.RSMessageHandler;
-import android.renderscript.Mesh.Primitive;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramVertexFixedFunction;
-
-import android.util.Log;
-
-
-public class UiTest implements RsBenchBaseTest{
-
-    private static final String TAG = "UiTest";
-    private static final String SAMPLE_TEXT = "Bench Test";
-    private static final String LIST_TEXT =
-      "This is a sample list of text to show in the list view";
-    private static int PARTICLES_COUNT = 12000;
-
-    private RenderScriptGL mRS;
-    private Resources mRes;
-
-    Font mFontSans;
-
-    private ScriptField_ListAllocs_s mTextureAllocs;
-    private ScriptField_ListAllocs_s mSampleTextAllocs;
-    private ScriptField_ListAllocs_s mSampleListViewAllocs;
-    private ScriptField_VpConsts mPvStarAlloc;
-    private ProgramVertexFixedFunction.Constants mPvProjectionAlloc;
-
-    private Mesh mSingleMesh;
-    private Mesh mParticlesMesh;
-
-    private ScriptC_ui_test mUiScript;
-
-    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
-
-    ScriptField_TestScripts_s.Item[] mTests;
-
-    private final String[] mNames = {
-        "UI test with icon display 10 by 10",
-        "UI test with icon display 100 by 100",
-        "UI test with image and text display 3 pages",
-        "UI test with image and text display 5 pages",
-        "UI test with list view",
-        "UI test with live wallpaper"
-    };
-
-    public UiTest() {
-    }
-
-    void addTest(int index, int testId, int user1, int user2, int user3) {
-        mTests[index] = new ScriptField_TestScripts_s.Item();
-        mTests[index].testScript = mUiScript;
-        mTests[index].testName = Allocation.createFromString(mRS,
-                                                             mNames[index],
-                                                             Allocation.USAGE_SCRIPT);
-        mTests[index].debugName = RsBenchRS.createZeroTerminatedAlloc(mRS,
-                                                                      mNames[index],
-                                                                      Allocation.USAGE_SCRIPT);
-
-        ScriptField_UiTestData_s.Item dataItem = new ScriptField_UiTestData_s.Item();
-        dataItem.testId = testId;
-        dataItem.user1 = user1;
-        dataItem.user2 = user2;
-        dataItem.user3 = user3;
-        ScriptField_UiTestData_s testData = new ScriptField_UiTestData_s(mRS, 1);
-        testData.set(dataItem, 0, true);
-        mTests[index].testData = testData.getAllocation();
-    }
-
-    public boolean init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
-        mSingleMesh = getSingleMesh(1, 1);  // a unit size mesh
-
-        initUiScript();
-        mTests = new ScriptField_TestScripts_s.Item[mNames.length];
-
-        int index = 0;
-
-        addTest(index++, 0, 0 /*meshMode*/, 0 /*unused*/, 0 /*unused*/);
-        addTest(index++, 0, 1 /*meshMode*/, 0 /*unused*/, 0 /*unused*/);
-        addTest(index++, 1, 7 /*wResolution*/, 5 /*hResolution*/, 0 /*meshMode*/);
-        addTest(index++, 1, 7 /*wResolution*/, 5 /*hResolution*/, 1 /*meshMode*/);
-        addTest(index++, 2, 0 /*unused*/, 0 /*unused*/, 0 /*unused*/);
-        addTest(index++, 3, 7 /*wResolution*/, 5 /*hResolution*/, 0 /*unused*/);
-
-        return true;
-    }
-
-    public ScriptField_TestScripts_s.Item[] getTests() {
-        return mTests;
-    }
-
-    public String[] getTestNames() {
-        return mNames;
-    }
-
-    private Allocation loadTextureRGB(int id) {
-        return Allocation.createFromBitmapResource(mRS, mRes, id,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    private Allocation loadTextureARGB(int id) {
-        Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
-        return Allocation.createFromBitmap(mRS, b,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-    }
-
-    private void createParticlesMesh() {
-        ScriptField_Particle p = new ScriptField_Particle(mRS, PARTICLES_COUNT);
-
-        final Mesh.AllocationBuilder meshBuilder = new Mesh.AllocationBuilder(mRS);
-        meshBuilder.addVertexAllocation(p.getAllocation());
-        final int vertexSlot = meshBuilder.getCurrentVertexTypeIndex();
-        meshBuilder.addIndexSetType(Primitive.POINT);
-        mParticlesMesh = meshBuilder.create();
-
-        mUiScript.set_gParticlesMesh(mParticlesMesh);
-        mUiScript.bind_Particles(p);
-    }
-
-    /**
-     * Create a mesh with a single quad for the given width and height.
-     */
-    private Mesh getSingleMesh(float width, float height) {
-        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
-                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
-        float xOffset = width/2;
-        float yOffset = height/2;
-        tmb.setTexture(0, 0);
-        tmb.addVertex(-1.0f * xOffset, -1.0f * yOffset);
-        tmb.setTexture(1, 0);
-        tmb.addVertex(xOffset, -1.0f * yOffset);
-        tmb.setTexture(1, 1);
-        tmb.addVertex(xOffset, yOffset);
-        tmb.setTexture(0, 1);
-        tmb.addVertex(-1.0f * xOffset, yOffset);
-        tmb.addTriangle(0, 3, 1);
-        tmb.addTriangle(1, 3, 2);
-        return tmb.create(true);
-    }
-
-    private Matrix4f getProjectionNormalized(int w, int h) {
-        // range -1,1 in the narrow axis at z = 0.
-        Matrix4f m1 = new Matrix4f();
-        Matrix4f m2 = new Matrix4f();
-
-        if(w > h) {
-            float aspect = ((float)w) / h;
-            m1.loadFrustum(-aspect,aspect,  -1,1,  1,100);
-        } else {
-            float aspect = ((float)h) / w;
-            m1.loadFrustum(-1,1, -aspect,aspect, 1,100);
-        }
-
-        m2.loadRotate(180, 0, 1, 0);
-        m1.loadMultiply(m1, m2);
-
-        m2.loadScale(-2, 2, 1);
-        m1.loadMultiply(m1, m2);
-
-        m2.loadTranslate(0, 0, 2);
-        m1.loadMultiply(m1, m2);
-        return m1;
-    }
-
-    private void updateProjectionMatrices() {
-        Matrix4f projNorm = getProjectionNormalized(1280, 720);
-        ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
-        i.Proj = projNorm;
-        i.MVP = projNorm;
-        mPvStarAlloc.set(i, 0, true);
-        mPvProjectionAlloc.setProjection(projNorm);
-    }
-
-    void initUiScript() {
-        mUiScript = new ScriptC_ui_test(mRS, mRes, R.raw.ui_test);
-
-        ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        colBuilder.setVaryingColor(false);
-        ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS);
-        texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                              ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        ProgramVertexFixedFunction progVertex = pvb.create();
-        ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction)progVertex).bindConstants(PVA);
-        Matrix4f proj = new Matrix4f();
-        proj.loadOrthoWindow(1280, 720);
-        PVA.setProjection(proj);
-
-        mUiScript.set_gProgVertex(progVertex);
-        mUiScript.set_gProgFragmentColor(colBuilder.create());
-        mUiScript.set_gProgFragmentTexture(texBuilder.create());
-        mUiScript.set_gProgStoreBlendAlpha(ProgramStore.BLEND_ALPHA_DEPTH_NONE(mRS));
-
-        mUiScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS));
-
-        mUiScript.set_gTexTorus(loadTextureRGB(R.drawable.torusmap));
-        mUiScript.set_gTexOpaque(loadTextureRGB(R.drawable.data));
-        mUiScript.set_gTexGlobe(loadTextureRGB(R.drawable.globe));
-        mUiScript.set_gSingleMesh(mSingleMesh);
-
-        // For GALAXY
-        ProgramStore.Builder psb = new ProgramStore.Builder(mRS);
-        psb.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
-        mRS.bindProgramStore(psb.create());
-
-        psb.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE);
-        mUiScript.set_gPSLights(psb.create());
-
-        // For Galaxy live wallpaper drawing
-        ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
-        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                           ProgramFragmentFixedFunction.Builder.Format.RGB, 0);
-        ProgramFragment pfb = builder.create();
-        pfb.bindSampler(Sampler.WRAP_NEAREST(mRS), 0);
-        mUiScript.set_gPFBackground(pfb);
-
-        builder = new ProgramFragmentFixedFunction.Builder(mRS);
-        builder.setPointSpriteTexCoordinateReplacement(true);
-        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
-                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        builder.setVaryingColor(true);
-        ProgramFragment pfs = builder.create();
-        pfs.bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0);
-        mUiScript.set_gPFStars(pfs);
-
-        mTextureAllocs = new ScriptField_ListAllocs_s(mRS, 100);
-        for (int i = 0; i < 100; i++) {
-            ScriptField_ListAllocs_s.Item texElem = new ScriptField_ListAllocs_s.Item();
-            texElem.item = loadTextureRGB(R.drawable.globe);
-            mTextureAllocs.set(texElem, i, false);
-        }
-        mTextureAllocs.copyAll();
-        mUiScript.bind_gTexList100(mTextureAllocs);
-
-        mSampleTextAllocs = new ScriptField_ListAllocs_s(mRS, 100);
-        for (int i = 0; i < 100; i++) {
-            ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item();
-            textElem.item = Allocation.createFromString(mRS, SAMPLE_TEXT, Allocation.USAGE_SCRIPT);
-            mSampleTextAllocs.set(textElem, i, false);
-        }
-        mSampleTextAllocs.copyAll();
-        mUiScript.bind_gSampleTextList100(mSampleTextAllocs);
-
-        mSampleListViewAllocs = new ScriptField_ListAllocs_s(mRS, 1000);
-        for (int i = 0; i < 1000; i++) {
-            ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item();
-            textElem.item = Allocation.createFromString(mRS, LIST_TEXT, Allocation.USAGE_SCRIPT);
-            mSampleListViewAllocs.set(textElem, i, false);
-        }
-        mSampleListViewAllocs.copyAll();
-        mUiScript.bind_gListViewText(mSampleListViewAllocs);
-
-        // For galaxy live wallpaper
-        mPvStarAlloc = new ScriptField_VpConsts(mRS, 1);
-        mUiScript.bind_vpConstants(mPvStarAlloc);
-        mPvProjectionAlloc = new ProgramVertexFixedFunction.Constants(mRS);
-        updateProjectionMatrices();
-
-        pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        ProgramVertex pvbp = pvb.create();
-        ((ProgramVertexFixedFunction)pvbp).bindConstants(mPvProjectionAlloc);
-        mUiScript.set_gPVBkProj(pvbp);
-
-        createParticlesMesh();
-
-        ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS);
-        String t =  "varying vec4 varColor;\n" +
-                    "varying vec2 varTex0;\n" +
-                    "void main() {\n" +
-                    "  float dist = ATTRIB_position.y;\n" +
-                    "  float angle = ATTRIB_position.x;\n" +
-                    "  float x = dist * sin(angle);\n" +
-                    "  float y = dist * cos(angle) * 0.892;\n" +
-                    "  float p = dist * 5.5;\n" +
-                    "  float s = cos(p);\n" +
-                    "  float t = sin(p);\n" +
-                    "  vec4 pos;\n" +
-                    "  pos.x = t * x + s * y;\n" +
-                    "  pos.y = s * x - t * y;\n" +
-                    "  pos.z = ATTRIB_position.z;\n" +
-                    "  pos.w = 1.0;\n" +
-                    "  gl_Position = UNI_MVP * pos;\n" +
-                    "  gl_PointSize = ATTRIB_color.a * 10.0;\n" +
-                    "  varColor.rgb = ATTRIB_color.rgb;\n" +
-                    "  varColor.a = 1.0;\n" +
-                    "}\n";
-        sb.setShader(t);
-        sb.addInput(mParticlesMesh.getVertexAllocation(0).getType().getElement());
-        sb.addConstant(mPvStarAlloc.getType());
-        ProgramVertex pvs = sb.create();
-        pvs.bindConstants(mPvStarAlloc.getAllocation(), 0);
-        mUiScript.set_gPVStars(pvs);
-
-        // For Galaxy live wallpaper
-        mUiScript.set_gTSpace(loadTextureRGB(R.drawable.space));
-        mUiScript.set_gTLight1(loadTextureRGB(R.drawable.light1));
-        mUiScript.set_gTFlares(loadTextureARGB(R.drawable.flares));
-
-        mUiScript.set_gFontSans(mFontSans);
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
deleted file mode 100644
index 281f830..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-#include "rs_graphics.rsh"
-#include "subtest_def.rsh"
-
-rs_program_vertex gProgVertex;
-rs_program_fragment gProgFragmentTexture;
-rs_program_fragment gProgFragmentTextureModulate;
-rs_program_fragment gProgFragmentMultitex;
-
-rs_program_store gProgStoreBlendNone;
-rs_program_store gProgStoreBlendAlpha;
-
-rs_allocation gTexOpaque;
-rs_allocation gTexTorus;
-rs_allocation gTexTransparent;
-rs_allocation gTexChecker;
-
-rs_sampler gLinearClamp;
-rs_sampler gLinearWrap;
-
-typedef struct FillTestData_s {
-    int testId;
-    int blend;
-    int quadCount;
-} FillTestData;
-FillTestData *gData;
-
-typedef struct FillTestFragData_s {
-    float4 modulate;
-} FillTestFragData;
-FillTestFragData *gFragData;
-
-static float gDt = 0.0f;
-
-void init() {
-}
-
-static int gRenderSurfaceW = 1280;
-static int gRenderSurfaceH = 720;
-
-static void bindProgramVertexOrtho() {
-    // Default vertex shader
-    rsgBindProgramVertex(gProgVertex);
-    // Setup the projection matrix
-    rs_matrix4x4 proj;
-    rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-}
-
-static void displaySingletexFill(bool blend, int quadCount, bool modulate) {
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    if (!blend) {
-        rsgBindProgramStore(gProgStoreBlendNone);
-    } else {
-        rsgBindProgramStore(gProgStoreBlendAlpha);
-    }
-    if (modulate) {
-        rsgBindProgramFragment(gProgFragmentTextureModulate);
-        rsgBindSampler(gProgFragmentTextureModulate, 0, gLinearClamp);
-        rsgBindTexture(gProgFragmentTextureModulate, 0, gTexOpaque);
-
-        gFragData->modulate.r = 0.8f;
-        gFragData->modulate.g = 0.7f;
-        gFragData->modulate.b = 0.8f;
-        gFragData->modulate.a = 0.5f;
-        rsgAllocationSyncAll(rsGetAllocation(gFragData));
-    } else {
-        rsgBindProgramFragment(gProgFragmentTexture);
-        rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-        rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
-    }
-
-    for (int i = 0; i < quadCount; i ++) {
-        float startX = 5 * i, startY = 5 * i;
-        float width = gRenderSurfaceW - startX, height = gRenderSurfaceH - startY;
-        rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                             startX, startY + height, 0, 0, 1,
-                             startX + width, startY + height, 0, 1, 1,
-                             startX + width, startY, 0, 1, 0);
-    }
-}
-
-static void displayMultitextureSample(bool blend, int quadCount) {
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    if (!blend) {
-        rsgBindProgramStore(gProgStoreBlendNone);
-    } else {
-        rsgBindProgramStore(gProgStoreBlendAlpha);
-    }
-    rsgBindProgramFragment(gProgFragmentMultitex);
-    rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp);
-    rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap);
-    rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp);
-    rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker);
-    rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus);
-    rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent);
-
-    for (int i = 0; i < quadCount; i ++) {
-        float startX = 10 * i, startY = 10 * i;
-        float width = gRenderSurfaceW - startX, height = gRenderSurfaceH - startY;
-        rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
-                             startX, startY + height, 0, 0, 1,
-                             startX + width, startY + height, 0, 1, 1,
-                             startX + width, startY, 0, 1, 0);
-    }
-}
-
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
-    TestData *testData = (TestData*)usrData;
-    gRenderSurfaceW = testData->renderSurfaceW;
-    gRenderSurfaceH = testData->renderSurfaceH;
-    gDt = testData->dt;
-
-    gData = (FillTestData*)v_in;
-
-    switch(gData->testId) {
-        case 0:
-            displayMultitextureSample(gData->blend == 1 ? true : false, gData->quadCount);
-            break;
-        case 1:
-            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount, false);
-            break;
-        case 2:
-            displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount, true);
-            break;
-        default:
-            rsDebug("Wrong test number", 0);
-            break;
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/mesh_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/mesh_test.rs
deleted file mode 100644
index d7e4857..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/mesh_test.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-#include "rs_graphics.rsh"
-#include "shader_def.rsh"
-#include "subtest_def.rsh"
-
-rs_program_vertex gProgVertex;
-rs_program_fragment gProgFragmentTexture;
-
-rs_program_store gProgStoreBlendNone;
-
-rs_allocation gTexOpaque;
-
-rs_mesh g10by10Mesh;
-rs_mesh g100by100Mesh;
-rs_mesh gWbyHMesh;
-
-rs_sampler gLinearClamp;
-static int gRenderSurfaceW;
-static int gRenderSurfaceH;
-
-static float gDt = 0;
-
-typedef struct MeshTestData_s {
-    int meshNum;
-} MeshTestData;
-MeshTestData *gData;
-
-void init() {
-}
-
-static void bindProgramVertexOrtho() {
-    // Default vertex shader
-    rsgBindProgramVertex(gProgVertex);
-    // Setup the projection matrix
-    rs_matrix4x4 proj;
-    rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-}
-
-static void displayMeshSamples(int meshNum) {
-
-    bindProgramVertexOrtho();
-    rs_matrix4x4 matrix;
-    rsMatrixLoadTranslate(&matrix, gRenderSurfaceW/2, gRenderSurfaceH/2, 0);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNone);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-
-    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
-
-    if (meshNum == 0) {
-        rsgDrawMesh(g10by10Mesh);
-    } else if (meshNum == 1) {
-        rsgDrawMesh(g100by100Mesh);
-    } else if (meshNum == 2) {
-        rsgDrawMesh(gWbyHMesh);
-    }
-}
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
-    TestData *testData = (TestData*)usrData;
-    gRenderSurfaceW = testData->renderSurfaceW;
-    gRenderSurfaceH = testData->renderSurfaceH;
-    gDt = testData->dt;
-
-    gData = (MeshTestData*)v_in;
-
-    displayMeshSamples(gData->meshNum);
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
deleted file mode 100644
index 43cf4e0..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright (C) 2010-2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-#include "rs_graphics.rsh"
-#include "shader_def.rsh"
-#include "subtest_def.rsh"
-
-/* Message sent from script to renderscript */
-const int RS_MSG_TEST_DONE = 100;
-const int RS_MSG_RESULTS_READY = 101;
-
-static const int gMaxModes = 64;
-int gMaxLoops = 1;
-int gDisplayMode = 1;
-
-// Allocation to write the results into
-static float gResultBuffer[gMaxModes];
-
-rs_font gFontSerif;
-rs_sampler gLinearClamp;
-
-rs_program_vertex gProgVertex;
-rs_program_fragment gProgFragmentTexture;
-
-rs_allocation gRenderBufferColor;
-rs_allocation gRenderBufferDepth;
-
-VertexShaderInputs *gVSInputs;
-
-typedef struct TestScripts_s {
-    rs_allocation testData;
-    rs_allocation testName;
-    rs_allocation debugName;
-    rs_script testScript;
-} TestScripts;
-TestScripts *gTestScripts;
-
-bool gLoadComplete = false;
-bool gPauseRendering = false;
-
-static float gDt = 0;
-
-void init() {
-}
-
-static int gRenderSurfaceW;
-static int gRenderSurfaceH;
-
-static void fillSurfaceParams(TestData *testData) {
-    testData->renderSurfaceW = gRenderSurfaceW;
-    testData->renderSurfaceH = gRenderSurfaceH;
-    testData->dt = gDt;
-}
-
-static void setupOffscreenTarget() {
-    rsgBindColorTarget(gRenderBufferColor, 0);
-    rsgBindDepthTarget(gRenderBufferDepth);
-}
-
-static void bindProgramVertexOrtho() {
-    // Default vertex shader
-    rsgBindProgramVertex(gProgVertex);
-    // Setup the projection matrix
-    rs_matrix4x4 proj;
-    rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-}
-
-static void runSubTest(int index) {
-    TestData testData;
-    fillSurfaceParams(&testData);
-
-    rs_allocation null_alloc = {0};
-    rsForEach(gTestScripts[index].testScript,
-              gTestScripts[index].testData,
-              null_alloc,
-              &testData,
-              sizeof(testData));
-}
-
-
-static bool checkInit() {
-
-    static int countdown = 3;
-
-    // Perform all the uploads so we only measure rendered time
-    if(countdown > 1) {
-        int testCount = rsAllocationGetDimX(rsGetAllocation(gTestScripts));
-        for(int i = 0; i < testCount; i ++) {
-            rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
-            runSubTest(i);
-            rsgFinish();
-        }
-        countdown --;
-        rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
-
-        rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
-        rsgBindFont(gFontSerif);
-        if (countdown == 1) {
-            rsgDrawText("Rendering", 50, 50);
-        } else {
-            rsgDrawText("Initializing", 50, 50);
-        }
-
-        return false;
-    }
-
-    return true;
-}
-
-static int benchMode = 0;
-static bool benchmarkSingleTest = false;
-static int runningLoops = 0;
-static bool sendMsgFlag = false;
-
-static bool gIsDebugMode = false;
-void setDebugMode(int testNumber) {
-    gIsDebugMode = true;
-    benchMode = testNumber;
-    rsgClearAllRenderTargets();
-}
-
-void setBenchmarkMode(int testNumber) {
-    gIsDebugMode = false;
-    if (testNumber == -1) {
-        benchmarkSingleTest = false;
-        benchMode = 0;
-    } else {
-        benchmarkSingleTest = true;
-        benchMode = testNumber;
-    }
-
-    runningLoops = 0;
-}
-
-static void drawOffscreenResult(int posX, int posY, int width, int height) {
-    bindProgramVertexOrtho();
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    rsgBindProgramFragment(gProgFragmentTexture);
-
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gRenderBufferColor);
-
-    float startX = posX, startY = posY;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
-                         startX, startY + height, 0, 0, 0,
-                         startX + width, startY + height, 0, 1, 0,
-                         startX + width, startY, 0, 1, 1);
-}
-
-static void benchmark() {
-
-    gDt = 1.0f / 60.0f;
-
-    rsgFinish();
-    int64_t start = rsUptimeMillis();
-
-    int drawPos = 0;
-    int frameCount = 100;
-    for(int i = 0; i < frameCount; i ++) {
-        setupOffscreenTarget();
-        gRenderSurfaceW = rsAllocationGetDimX(gRenderBufferColor);
-        gRenderSurfaceH = rsAllocationGetDimY(gRenderBufferColor);
-        rsgClearColor(0.1f, 0.1f, 0.1f, 1.0f);
-        rsgClearDepth(1.0f);
-
-        runSubTest(benchMode);
-        rsgClearAllRenderTargets();
-        gRenderSurfaceW = rsgGetWidth();
-        gRenderSurfaceH = rsgGetHeight();
-        int size = 8;
-        // draw each frame at (8, 3/4 gRenderSurfaceH) with size
-        drawOffscreenResult((drawPos+=size)%gRenderSurfaceW, (gRenderSurfaceH * 3) / 4, size, size);
-    }
-
-    rsgFinish();
-
-    int64_t end = rsUptimeMillis();
-    float fps = (float)(frameCount) / ((float)(end - start)*0.001f);
-    const char *testName = rsGetElementAt(gTestScripts[benchMode].debugName, 0);
-    rsDebug(testName, fps);
-
-    gResultBuffer[benchMode] = fps;
-    int bufferW = rsAllocationGetDimX(gRenderBufferColor);
-    int bufferH = rsAllocationGetDimY(gRenderBufferColor);
-
-    int quadW = gRenderSurfaceW / 2;
-    int quadH = (quadW * bufferH) / bufferW;
-    drawOffscreenResult(0, 0, quadW, quadH);
-
-    int left = 0, right = 0, top = 0, bottom = 0;
-    uint height = rsgGetHeight();
-    rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
-    rsgBindFont(gFontSerif);
-    rsgMeasureText(gTestScripts[benchMode].testName, &left, &right, &top, &bottom);
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgDrawText(gTestScripts[benchMode].testName, 2 -left, height - 2 + bottom);
-
-    if (benchmarkSingleTest) {
-        return;
-    }
-
-    benchMode ++;
-    int testCount = rsAllocationGetDimX(rsGetAllocation(gTestScripts));
-    if (benchMode == testCount) {
-        rsSendToClientBlocking(RS_MSG_RESULTS_READY, gResultBuffer, testCount*sizeof(float));
-        benchMode = 0;
-        runningLoops++;
-        if ((gMaxLoops > 0) && (runningLoops > gMaxLoops) && !sendMsgFlag) {
-            //Notifiy the test to stop and get results
-            rsDebug("gMaxLoops and runningLoops: ", gMaxLoops, runningLoops);
-            rsSendToClientBlocking(RS_MSG_TEST_DONE);
-            sendMsgFlag = true;
-        }
-    }
-}
-
-static void debug() {
-    gDt = rsGetDt();
-    runSubTest(benchMode);
-}
-
-int root(void) {
-    gRenderSurfaceW = rsgGetWidth();
-    gRenderSurfaceH = rsgGetHeight();
-    rsgClearColor(0.2f, 0.2f, 0.2f, 1.0f);
-    rsgClearDepth(1.0f);
-
-    if (!gLoadComplete) {
-        rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
-        rsgBindFont(gFontSerif);
-        rsgDrawText("Loading", 50, 50);
-        return 0;
-    }
-
-    if(!checkInit()) {
-        return 1;
-    }
-
-    if (gPauseRendering) {
-        rsgDrawText("Paused", 50, 50);
-        return 30;
-    }
-    if (gIsDebugMode) {
-        debug();
-    } else {
-        benchmark();
-    }
-
-    return 1;
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/shader_def.rsh b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/shader_def.rsh
deleted file mode 100644
index 648359c..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/shader_def.rsh
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2009 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.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-typedef struct VertexShaderConstants_s {
-    rs_matrix4x4 model;
-    rs_matrix4x4 proj;
-    float4 light0_Posision;
-    float light0_Diffuse;
-    float light0_Specular;
-    float light0_CosinePower;
-
-    float4 light1_Posision;
-    float light1_Diffuse;
-    float light1_Specular;
-    float light1_CosinePower;
-} VertexShaderConstants;
-
-typedef struct VertexShaderConstants3_s {
-    rs_matrix4x4 model;
-    rs_matrix4x4 proj;
-    float time;
-} VertexShaderConstants3;
-
-
-typedef struct FragentShaderConstants_s {
-    float4 light0_DiffuseColor;
-    float4 light0_SpecularColor;
-
-    float4 light1_DiffuseColor;
-    float4 light1_SpecularColor;
-} FragentShaderConstants;
-
-typedef struct FragentShaderConstants2_s {
-    float4 light_DiffuseColor[2];
-    float4 light_SpecularColor[2];
-} FragentShaderConstants2;
-
-typedef struct FragentShaderConstants3_s {
-    float4 light0_DiffuseColor;
-    float4 light0_SpecularColor;
-    float4 light0_Posision;
-    float light0_Diffuse;
-    float light0_Specular;
-    float light0_CosinePower;
-
-    float4 light1_DiffuseColor;
-    float4 light1_SpecularColor;
-    float4 light1_Posision;
-    float light1_Diffuse;
-    float light1_Specular;
-    float light1_CosinePower;
-} FragentShaderConstants3;
-
-typedef struct VertexShaderInputs_s {
-    float4 position;
-    float3 normal;
-    float2 texture0;
-} VertexShaderInputs;
-
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh
deleted file mode 100644
index 43658b1..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-typedef struct TestData_s {
-    int renderSurfaceW;
-    int renderSurfaceH;
-    float dt;
-} TestData;
-
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
deleted file mode 100644
index 0f50828..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-#include "rs_graphics.rsh"
-#include "subtest_def.rsh"
-
-rs_font gFontSans;
-rs_font gFontSerif;
-
-typedef struct TextTestData_s {
-    int fillNum;
-} TextTestData;
-TextTestData *gData;
-
-void init() {
-}
-
-static int gRenderSurfaceW = 1280;
-static int gRenderSurfaceH = 720;
-
-static const char *sampleText = "This is a sample of small text for performace";
-// Offsets for multiple layer of text
-static int textOffsets[] = { 0,  0, -5, -5, 5,  5, -8, -8, 8,  8};
-static float textColors[] = {1.0f, 1.0f, 1.0f, 1.0f,
-                             0.5f, 0.7f, 0.5f, 1.0f,
-                             0.7f, 0.5f, 0.5f, 1.0f,
-                             0.5f, 0.5f, 0.7f, 1.0f,
-                             0.5f, 0.6f, 0.7f, 1.0f,
-};
-
-static void displayFontSamples(int fillNum) {
-
-    rs_font fonts[5];
-    fonts[0] = gFontSans;
-    fonts[1] = gFontSerif;
-    fonts[2] = gFontSans;
-    fonts[3] = gFontSerif;
-    fonts[4] = gFontSans;
-
-    uint height = gRenderSurfaceH;
-    int left = 0, right = 0, top = 0, bottom = 0;
-    rsgMeasureText(sampleText, &left, &right, &top, &bottom);
-
-    int textHeight = top - bottom;
-    int textWidth = right - left;
-    int numVerticalLines = height / textHeight;
-    int yPos = top;
-
-    int xOffset = 0, yOffset = 0;
-    for(int fillI = 0; fillI < fillNum; fillI ++) {
-        rsgBindFont(fonts[fillI]);
-        xOffset = textOffsets[fillI * 2];
-        yOffset = textOffsets[fillI * 2 + 1];
-        float *colPtr = textColors + fillI * 4;
-        rsgFontColor(colPtr[0], colPtr[1], colPtr[2], colPtr[3]);
-        for (int h = 0; h < 4; h ++) {
-            yPos = top + yOffset;
-            for (int v = 0; v < numVerticalLines; v ++) {
-                rsgDrawText(sampleText, xOffset + textWidth * h, yPos);
-                yPos += textHeight;
-            }
-        }
-    }
-}
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
-    TestData *testData = (TestData*)usrData;
-    gRenderSurfaceW = testData->renderSurfaceW;
-    gRenderSurfaceH = testData->renderSurfaceH;
-
-    gData = (TextTestData*)v_in;
-
-    displayFontSamples(gData->fillNum);
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs
deleted file mode 100644
index 853a05d..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-#include "rs_graphics.rsh"
-#include "subtest_def.rsh"
-#include "shader_def.rsh"
-
-rs_program_vertex gProgVertex;
-rs_program_fragment gProgFragmentColor;
-rs_program_fragment gProgFragmentTexture;
-
-rs_program_store gProgStoreBlendNoneDepth;
-rs_mesh gTorusMesh;
-
-rs_program_raster gCullBack;
-rs_program_raster gCullFront;
-
-// Custom vertex shader compunents
-VertexShaderConstants *gVSConstants;
-FragentShaderConstants *gFSConstants;
-VertexShaderConstants3 *gVSConstPixel;
-FragentShaderConstants3 *gFSConstPixel;
-
-// Custom shaders we use for lighting
-rs_program_vertex gProgVertexCustom;
-rs_program_fragment gProgFragmentCustom;
-
-rs_sampler gLinearClamp;
-rs_allocation gTexTorus;
-
-rs_program_vertex gProgVertexPixelLight;
-rs_program_vertex gProgVertexPixelLightMove;
-rs_program_fragment gProgFragmentPixelLight;
-
-typedef struct TorusTestData_s {
-    int testId;
-    int user1;
-    int user2;
-} TorusTestData;
-TorusTestData *gData;
-
-static float gDt = 0.0f;
-
-static int gRenderSurfaceW;
-static int gRenderSurfaceH;
-
-
-static float gTorusRotation = 0;
-static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
-    if (buffer == 0) {
-        rsgProgramVertexLoadModelMatrix(matrix);
-    } else {
-        rsgAllocationSyncAll(rsGetAllocation(buffer));
-    }
-}
-
-static void drawToruses(int numMeshes, rs_matrix4x4 *matrix, void *buffer) {
-
-    if (numMeshes == 1) {
-        rsMatrixLoadTranslate(matrix, 0.0f, 0.0f, -7.5f);
-        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        updateModelMatrix(matrix, buffer);
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    if (numMeshes == 2) {
-        rsMatrixLoadTranslate(matrix, -1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        updateModelMatrix(matrix, buffer);
-        rsgDrawMesh(gTorusMesh);
-
-        rsMatrixLoadTranslate(matrix, 1.6f, 0.0f, -7.5f);
-        rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-        updateModelMatrix(matrix, buffer);
-        rsgDrawMesh(gTorusMesh);
-        return;
-    }
-
-    float startX = -5.0f;
-    float startY = -1.5f;
-    float startZ = -15.0f;
-    float dist = 3.2f;
-
-    for (int h = 0; h < 4; h ++) {
-        for (int v = 0; v < 2; v ++) {
-            // Position our model on the screen
-            rsMatrixLoadTranslate(matrix, startX + dist * h, startY + dist * v, startZ);
-            rsMatrixRotate(matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
-            updateModelMatrix(matrix, buffer);
-            rsgDrawMesh(gTorusMesh);
-        }
-    }
-}
-
-
-// Quick hack to get some geometry numbers
-static void displaySimpleGeoSamples(bool useTexture, int numMeshes) {
-    rsgBindProgramVertex(gProgVertex);
-    rsgBindProgramRaster(gCullBack);
-    // Setup the projection matrix with 30 degree field of view
-    rs_matrix4x4 proj;
-    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    if (useTexture) {
-        rsgBindProgramFragment(gProgFragmentTexture);
-    } else {
-        rsgBindProgramFragment(gProgFragmentColor);
-        rsgProgramFragmentConstantColor(gProgFragmentColor, 0.1, 0.7, 0.1, 1);
-    }
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
-
-    // Apply a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    rs_matrix4x4 matrix;
-    drawToruses(numMeshes, &matrix, 0);
-}
-
-float gLight0Rotation = 0;
-float gLight1Rotation = 0;
-
-static void setupCustomShaderLights() {
-    float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
-    float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
-    float4 light0DiffCol = {0.9f, 0.7f, 0.7f, 1.0f};
-    float4 light0SpecCol = {0.9f, 0.6f, 0.6f, 1.0f};
-    float4 light1DiffCol = {0.5f, 0.5f, 0.9f, 1.0f};
-    float4 light1SpecCol = {0.5f, 0.5f, 0.9f, 1.0f};
-
-    gLight0Rotation += 50.0f * gDt;
-    if (gLight0Rotation > 360.0f) {
-        gLight0Rotation -= 360.0f;
-    }
-    gLight1Rotation -= 50.0f * gDt;
-    if (gLight1Rotation > 360.0f) {
-        gLight1Rotation -= 360.0f;
-    }
-
-    rs_matrix4x4 l0Mat;
-    rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f);
-    light0Pos = rsMatrixMultiply(&l0Mat, light0Pos);
-    rs_matrix4x4 l1Mat;
-    rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f);
-    light1Pos = rsMatrixMultiply(&l1Mat, light1Pos);
-
-    // Set light 0 properties
-    gVSConstants->light0_Posision = light0Pos;
-    gVSConstants->light0_Diffuse = 1.0f;
-    gVSConstants->light0_Specular = 0.5f;
-    gVSConstants->light0_CosinePower = 10.0f;
-    // Set light 1 properties
-    gVSConstants->light1_Posision = light1Pos;
-    gVSConstants->light1_Diffuse = 1.0f;
-    gVSConstants->light1_Specular = 0.7f;
-    gVSConstants->light1_CosinePower = 25.0f;
-    rsgAllocationSyncAll(rsGetAllocation(gVSConstants));
-
-    // Update fragment shader constants
-    // Set light 0 colors
-    gFSConstants->light0_DiffuseColor = light0DiffCol;
-    gFSConstants->light0_SpecularColor = light0SpecCol;
-    // Set light 1 colors
-    gFSConstants->light1_DiffuseColor = light1DiffCol;
-    gFSConstants->light1_SpecularColor = light1SpecCol;
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstants));
-
-    // Set light 0 properties for per pixel lighting
-    gFSConstPixel->light0_Posision = light0Pos;
-    gFSConstPixel->light0_Diffuse = 1.0f;
-    gFSConstPixel->light0_Specular = 0.5f;
-    gFSConstPixel->light0_CosinePower = 10.0f;
-    gFSConstPixel->light0_DiffuseColor = light0DiffCol;
-    gFSConstPixel->light0_SpecularColor = light0SpecCol;
-    // Set light 1 properties
-    gFSConstPixel->light1_Posision = light1Pos;
-    gFSConstPixel->light1_Diffuse = 1.0f;
-    gFSConstPixel->light1_Specular = 0.7f;
-    gFSConstPixel->light1_CosinePower = 25.0f;
-    gFSConstPixel->light1_DiffuseColor = light1DiffCol;
-    gFSConstPixel->light1_SpecularColor = light1SpecCol;
-    rsgAllocationSyncAll(rsGetAllocation(gFSConstPixel));
-}
-
-static void displayCustomShaderSamples(int numMeshes) {
-
-    // Update vertex shader constants
-    // Load model matrix
-    // Apply a rotation to our mesh
-    gTorusRotation += 50.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    // Setup the projection matrix
-    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
-    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
-    setupCustomShaderLights();
-
-    rsgBindProgramVertex(gProgVertexCustom);
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentCustom);
-    rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentCustom, 0, gTexTorus);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-
-    drawToruses(numMeshes, &gVSConstants->model, gVSConstants);
-}
-
-static void displayPixelLightSamples(int numMeshes, bool heavyVertex) {
-
-    // Update vertex shader constants
-    // Load model matrix
-    // Apply a rotation to our mesh
-    gTorusRotation += 30.0f * gDt;
-    if (gTorusRotation > 360.0f) {
-        gTorusRotation -= 360.0f;
-    }
-
-    gVSConstPixel->time = rsUptimeMillis()*0.005;
-
-    // Setup the projection matrix
-    float aspect = (float)gRenderSurfaceW / (float)gRenderSurfaceH;
-    rsMatrixLoadPerspective(&gVSConstPixel->proj, 30.0f, aspect, 0.1f, 100.0f);
-    setupCustomShaderLights();
-
-    if (heavyVertex) {
-        rsgBindProgramVertex(gProgVertexPixelLightMove);
-    } else {
-        rsgBindProgramVertex(gProgVertexPixelLight);
-    }
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendNoneDepth);
-    rsgBindProgramFragment(gProgFragmentPixelLight);
-    rsgBindSampler(gProgFragmentPixelLight, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentPixelLight, 0, gTexTorus);
-
-    // Use back face culling
-    rsgBindProgramRaster(gCullBack);
-
-    drawToruses(numMeshes, &gVSConstPixel->model, gVSConstPixel);
-}
-
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
-    TestData *testData = (TestData*)usrData;
-    gRenderSurfaceW = testData->renderSurfaceW;
-    gRenderSurfaceH = testData->renderSurfaceH;
-    gDt = testData->dt;
-
-    gData = (TorusTestData*)v_in;
-
-    switch(gData->testId) {
-        case 0:
-            displaySimpleGeoSamples(gData->user1 == 1 ? true : false, gData->user2);
-            break;
-        case 1:
-            displayCustomShaderSamples(gData->user1);
-            break;
-        case 2:
-            displayPixelLightSamples(gData->user1, gData->user2 == 1 ? true : false);
-            break;
-        default:
-            rsDebug("Wrong test number", gData->testId);
-            break;
-    }
-}
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs
deleted file mode 100644
index e87db39..0000000
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs
+++ /dev/null
@@ -1,442 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.perftest)
-
-#include "rs_graphics.rsh"
-#include "shader_def.rsh"
-#include "subtest_def.rsh"
-
-// Parameters for galaxy live wallpaper
-rs_allocation gTSpace;
-rs_allocation gTLight1;
-rs_allocation gTFlares;
-rs_mesh gParticlesMesh;
-
-rs_program_fragment gPFBackground;
-rs_program_fragment gPFStars;
-rs_program_vertex gPVStars;
-rs_program_vertex gPVBkProj;
-rs_program_store gPSLights;
-
-float gXOffset = 0.5f;
-
-#define ELLIPSE_RATIO 0.892f
-#define PI 3.1415f
-#define TWO_PI 6.283f
-#define ELLIPSE_TWIST 0.023333333f
-
-static float angle = 50.f;
-static int gOldWidth;
-static int gOldHeight;
-static int gWidth;
-static int gHeight;
-static float gSpeed[12000];
-static int gGalaxyRadius = 300;
-static rs_allocation gParticlesBuffer;
-
-typedef struct __attribute__((packed, aligned(4))) Particle {
-    uchar4 color;
-    float3 position;
-} Particle_t;
-Particle_t *Particles;
-
-typedef struct VpConsts {
-    rs_matrix4x4 Proj;
-    rs_matrix4x4 MVP;
-} VpConsts_t;
-VpConsts_t *vpConstants;
-// End of parameters for galaxy live wallpaper
-
-rs_program_vertex gProgVertex;
-rs_program_fragment gProgFragmentColor;
-rs_program_fragment gProgFragmentTexture;
-
-rs_program_store gProgStoreBlendAlpha;
-
-rs_allocation gTexOpaque;
-rs_allocation gTexTorus;
-rs_allocation gTexGlobe;
-
-typedef struct ListAllocs_s {
-    rs_allocation item;
-} ListAllocs;
-
-ListAllocs *gTexList100;
-ListAllocs *gSampleTextList100;
-ListAllocs *gListViewText;
-
-rs_mesh gSingleMesh;
-
-rs_font gFontSans;
-
-rs_sampler gLinearClamp;
-
-typedef struct UiTestData_s {
-    int testId;
-    int user1;
-    int user2;
-    int user3;
-} UiTestData;
-UiTestData *gData;
-
-static float gDt = 0;
-
-
-void init() {
-}
-
-static int gRenderSurfaceW;
-static int gRenderSurfaceH;
-
-static void bindProgramVertexOrtho() {
-    // Default vertex shader
-    rsgBindProgramVertex(gProgVertex);
-    // Setup the projection matrix
-    rs_matrix4x4 proj;
-    rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-}
-
-/**
-  * Methods to draw the galaxy live wall paper
-  */
-static float mapf(float minStart, float minStop, float maxStart, float maxStop, float value) {
-    return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
-}
-
-/**
- * Helper function to generate the stars.
- */
-static float randomGauss() {
-    float x1;
-    float x2;
-    float w = 2.f;
-
-    while (w >= 1.0f) {
-        x1 = rsRand(2.0f) - 1.0f;
-        x2 = rsRand(2.0f) - 1.0f;
-        w = x1 * x1 + x2 * x2;
-    }
-
-    w = sqrt(-2.0f * log(w) / w);
-    return x1 * w;
-}
-
-/**
- * Generates the properties for a given star.
- */
-static void createParticle(Particle_t *part, int idx, float scale) {
-    float d = fabs(randomGauss()) * gGalaxyRadius * 0.5f + rsRand(64.0f);
-    float id = d / gGalaxyRadius;
-    float z = randomGauss() * 0.4f * (1.0f - id);
-
-    if (d < gGalaxyRadius * 0.33f) {
-        part->color.x = (uchar) (220 + id * 35);
-        part->color.y = 220;
-        part->color.z = 220;
-    } else {
-        part->color.x = 180;
-        part->color.y = 180;
-        part->color.z = (uchar) clamp(140.f + id * 115.f, 140.f, 255.f);
-    }
-    // Stash point size * 10 in Alpha
-    part->color.w = (uchar) (rsRand(1.2f, 2.1f) * 60);
-
-    if (d > gGalaxyRadius * 0.15f) {
-        z *= 0.6f * (1.0f - id);
-    } else {
-        z *= 0.72f;
-    }
-
-    // Map to the projection coordinates (viewport.x = -1.0 -> 1.0)
-    d = mapf(-4.0f, gGalaxyRadius + 4.0f, 0.0f, scale, d);
-
-    part->position.x = rsRand(TWO_PI);
-    part->position.y = d;
-    gSpeed[idx] = rsRand(0.0015f, 0.0025f) * (0.5f + (scale / d)) * 0.8f;
-
-    part->position.z = z / 5.0f;
-}
-
-/**
- * Initialize all the starts, called from Java
- */
-void initParticles() {
-    Particle_t *part = Particles;
-    float scale = gGalaxyRadius / (gWidth * 0.5f);
-    int count = rsAllocationGetDimX(gParticlesBuffer);
-    for (int i = 0; i < count; i ++) {
-        createParticle(part, i, scale);
-        part++;
-    }
-}
-
-static void drawSpace() {
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindTexture(gPFBackground, 0, gTSpace);
-    rsgDrawQuadTexCoords(
-            0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-            gWidth, 0.0f, 0.0f, 2.0f, 1.0f,
-            gWidth, gHeight, 0.0f, 2.0f, 0.0f,
-            0.0f, gHeight, 0.0f, 0.0f, 0.0f);
-}
-
-static void drawLights() {
-    rsgBindProgramVertex(gPVBkProj);
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindTexture(gPFBackground, 0, gTLight1);
-
-    float scale = 512.0f / gWidth;
-    float x = -scale - scale * 0.05f;
-    float y = -scale;
-
-    scale *= 2.0f;
-
-    rsgDrawQuad(x, y, 0.0f,
-             x + scale * 1.1f, y, 0.0f,
-             x + scale * 1.1f, y + scale, 0.0f,
-             x, y + scale, 0.0f);
-}
-
-static void drawParticles(float offset) {
-    float a = offset * angle;
-    float absoluteAngle = fabs(a);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, 10.0f - 6.0f * absoluteAngle / 50.0f);
-    if (gHeight > gWidth) {
-        rsMatrixScale(&matrix, 6.6f, 6.0f, 1.0f);
-    } else {
-        rsMatrixScale(&matrix, 12.6f, 12.0f, 1.0f);
-    }
-    rsMatrixRotate(&matrix, absoluteAngle, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, a, 0.0f, 0.4f, 0.1f);
-    rsMatrixLoad(&vpConstants->MVP, &vpConstants->Proj);
-    rsMatrixMultiply(&vpConstants->MVP, &matrix);
-    rsgAllocationSyncAll(rsGetAllocation(vpConstants));
-
-    rsgBindProgramVertex(gPVStars);
-    rsgBindProgramFragment(gPFStars);
-    rsgBindProgramStore(gPSLights);
-    rsgBindTexture(gPFStars, 0, gTFlares);
-
-    Particle_t *vtx = Particles;
-    int count = rsAllocationGetDimX(gParticlesBuffer);
-    for (int i = 0; i < count; i++) {
-        vtx->position.x = vtx->position.x + gSpeed[i];
-        vtx++;
-    }
-
-    rsgDrawMesh(gParticlesMesh);
-}
-/* end of methods for drawing galaxy */
-
-// Display sample images in a mesh with different texture
-static void displayIcons(int meshMode) {
-    bindProgramVertexOrtho();
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendAlpha);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
-    rsgDrawQuadTexCoords(
-            0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-            0.0f, gRenderSurfaceH, 0.0f, 0.0f, 1.0f,
-            gRenderSurfaceW, gRenderSurfaceH, 0.0f, 1.0f, 1.0f,
-            gRenderSurfaceW, 0.0f, 0.0f, 1.0f, 0.0f);
-
-    int meshCount = (int)pow(10.0f, (float)(meshMode + 1));
-
-    float wSize = gRenderSurfaceW/(float)meshCount;
-    float hSize = gRenderSurfaceH/(float)meshCount;
-    rs_matrix4x4 matrix;
-    rsMatrixLoadScale(&matrix, wSize, hSize, 1.0);
-
-    float yPos = 0;
-    float yPad = hSize / 2;
-    float xPad = wSize / 2;
-    for (int y = 0; y < meshCount; y++) {
-        yPos = y * hSize + yPad;
-        float xPos = 0;
-        for (int x = 0; x < meshCount; x++) {
-            xPos = x * wSize + xPad;
-            rs_matrix4x4 transMatrix;
-            rsMatrixLoadTranslate(&transMatrix, xPos, yPos, 0);
-            rsMatrixMultiply(&transMatrix, &matrix);
-            rsgProgramVertexLoadModelMatrix(&transMatrix);
-            int i = (x + y * meshCount) % 100;
-            rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item);
-            rsgDrawMesh(gSingleMesh);
-        }
-    }
-}
-
-// Draw meshes in a single page with top left corner coordinates (xStart, yStart)
-static void drawMeshInPage(float xStart, float yStart, int wResolution, int hResolution) {
-    // Draw wResolution * hResolution meshes in one page
-    float wMargin = 100.0f;
-    float hMargin = 100.0f;
-    float xPad = 50.0f;
-    float yPad = 20.0f;
-    float size = 100.0f;  // size of images
-
-    // font info
-    rs_font font = gFontSans;
-    rsgBindFont(font);
-    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-
-    // Measure text size
-    int left = 0, right = 0, top = 0, bottom = 0;
-    rsgMeasureText(gSampleTextList100[0].item, &left, &right, &top, &bottom);
-    float textHeight = (float)(top - bottom);
-
-    rs_matrix4x4 matrix;
-    rsMatrixLoadScale(&matrix, size, size, 1.0);
-
-    for (int y = 0; y < hResolution; y++) {
-        float yPos = yStart + hMargin + y * size + y * yPad;
-        for (int x = 0; x < wResolution; x++) {
-            float xPos = xStart + wMargin + x * size + x * xPad;
-
-            rs_matrix4x4 transMatrix;
-            rsMatrixLoadTranslate(&transMatrix, xPos + size/2, yPos + size/2, 0);
-            rsMatrixMultiply(&transMatrix, &matrix);  // scale the mesh
-            rsgProgramVertexLoadModelMatrix(&transMatrix);
-
-            int i = (y * wResolution + x) % 100;
-            rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item);
-            rsgDrawMesh(gSingleMesh);
-            rsgDrawText(gSampleTextList100[i].item, xPos, yPos + size + yPad/2 + textHeight);
-        }
-    }
-}
-
-// Display both images and text as shown in launcher and homepage
-// meshMode will decide how many pages we draw
-// meshMode = 0: draw 3 pages of meshes
-// meshMode = 1: draw 5 pages of meshes
-static void displayImageWithText(int wResolution, int hResolution, int meshMode) {
-    bindProgramVertexOrtho();
-
-    // Fragment shader with texture
-    rsgBindProgramStore(gProgStoreBlendAlpha);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-
-    drawMeshInPage(0, 0, wResolution, hResolution);
-    drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-    drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-    if (meshMode == 1) {
-        // draw another two pages of meshes
-        drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-        drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-    }
-}
-
-// Display a list of text as the list view
-static void displayListView() {
-    // set text color
-    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
-    rsgBindFont(gFontSans);
-
-    // get the size of the list
-    rs_allocation textAlloc;
-    textAlloc = rsGetAllocation(gListViewText);
-    int allocSize = rsAllocationGetDimX(textAlloc);
-
-    int listItemHeight = 80;
-    int yOffset = listItemHeight;
-
-    // set the color for the list divider
-    rsgBindProgramFragment(gProgFragmentColor);
-    rsgProgramFragmentConstantColor(gProgFragmentColor, 1.0, 1.0, 1.0, 1);
-
-    // draw the list with divider
-    for (int i = 0; i < allocSize; i++) {
-        if (yOffset - listItemHeight > gRenderSurfaceH) {
-            break;
-        }
-        rsgDrawRect(0, yOffset - 1, gRenderSurfaceW, yOffset, 0);
-        rsgDrawText(gListViewText[i].item, 20, yOffset - 10);
-        yOffset += listItemHeight;
-    }
-}
-
-static void drawGalaxy() {
-    rsgClearColor(0.f, 0.f, 0.f, 1.f);
-    gParticlesBuffer = rsGetAllocation(Particles);
-    rsgBindProgramFragment(gPFBackground);
-
-    gWidth = rsgGetWidth();
-    gHeight = rsgGetHeight();
-    if ((gWidth != gOldWidth) || (gHeight != gOldHeight)) {
-        initParticles();
-        gOldWidth = gWidth;
-        gOldHeight = gHeight;
-    }
-
-    float offset = mix(-1.0f, 1.0f, gXOffset);
-    drawSpace();
-    drawParticles(offset);
-    drawLights();
-}
-
-// Display images and text with live wallpaper in the background
-static void displayLiveWallPaper(int wResolution, int hResolution) {
-    bindProgramVertexOrtho();
-
-    drawGalaxy();
-
-    rsgBindProgramVertex(gProgVertex);
-    rsgBindProgramStore(gProgStoreBlendAlpha);
-    rsgBindProgramFragment(gProgFragmentTexture);
-    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
-
-    drawMeshInPage(0, 0, wResolution, hResolution);
-    drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-    drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-    drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-    drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution);
-}
-
-void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
-    TestData *testData = (TestData*)usrData;
-    gRenderSurfaceW = testData->renderSurfaceW;
-    gRenderSurfaceH = testData->renderSurfaceH;
-    gDt = testData->dt;
-
-    gData = (UiTestData*)v_in;
-
-    switch(gData->testId) {
-        case 0:
-            displayIcons(gData->user1);
-            break;
-        case 1:
-            displayImageWithText(gData->user1, gData->user2, gData->user3);
-            break;
-        case 2:
-            displayListView();
-            break;
-        case 3:
-            displayLiveWallPaper(gData->user1, gData->user2);
-            break;
-        default:
-            rsDebug("Wrong test number", 0);
-            break;
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/Android.mk b/tests/RenderScriptTests/SceneGraph/Android.mk
deleted file mode 100644
index 6047305..0000000
--- a/tests/RenderScriptTests/SceneGraph/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_SDK_VERSION := 17
-
-LOCAL_PACKAGE_NAME := SceneGraphTest
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
deleted file mode 100644
index 67af0fa..0000000
--- a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.testapp">
-    <uses-permission
-        android:name="android.permission.INTERNET" />
-    <application android:label="SceneGraphTest">
-        <activity android:name="TestApp"
-                  android:label="SceneGraphTest">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity android:name="SimpleApp"
-                  android:label="SimpleSceneGraph">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity android:name="FileSelector"
-                  android:label="FileSelector"
-                  android:hardwareAccelerated="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/RenderScriptTests/SceneGraph/assets/blue.jpg b/tests/RenderScriptTests/SceneGraph/assets/blue.jpg
deleted file mode 100644
index 494e77a..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/blue.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg b/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg
deleted file mode 100644
index 2fcecb0..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/carbonfiber.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/green.jpg b/tests/RenderScriptTests/SceneGraph/assets/green.jpg
deleted file mode 100644
index a86a754..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/green.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/grey.jpg b/tests/RenderScriptTests/SceneGraph/assets/grey.jpg
deleted file mode 100644
index 5870b1a..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/grey.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orange.jpg b/tests/RenderScriptTests/SceneGraph/assets/orange.jpg
deleted file mode 100644
index 7dbe942..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/orange.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d
deleted file mode 100644
index 07318ae..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae b/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae
deleted file mode 100644
index 7eef443..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/orientation_test.dae
+++ /dev/null
@@ -1,1102 +0,0 @@
-<?xml version="1.0" ?>
-<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
-    <asset>
-        <contributor>
-            <author>alexst</author>
-            <authoring_tool>OpenCOLLADA2010</authoring_tool>
-            <comments>ColladaMaya export options: bakeTransforms=0;relativePaths=0;copyTextures=0;exportTriangles=1;exportCgfxFileReferences=0; isSampling=0;curveConstrainSampling=0;removeStaticCurves=1;exportPolygonMeshes=1;exportLights=1; exportCameras=1;exportJointsAndSkin=1;exportAnimations=0;exportInvisibleNodes=0;exportDefaultCameras=0; exportTexCoords=1;exportNormals=1;exportNormalsPerVertex=1;exportVertexColors=0;exportVertexColorsPerVertex=0; exportTexTangents=0;exportTangents=0;exportReferencedMaterials=1;exportMaterialsOnly=0; exportXRefs=1;dereferenceXRefs=1;exportCameraAsLookat=0;cameraXFov=0;cameraYFov=1;doublePrecision=0</comments>
-            <source_data>file:///Volumes/Android/art/orientation_test.mb</source_data>
-        </contributor>
-        <created>2011-09-30T15:31:38</created>
-        <modified>2011-09-30T15:31:38</modified>
-        <unit meter="0.01" name="centimeter" />
-        <up_axis>Y_UP</up_axis>
-    </asset>
-    <library_cameras>
-        <camera id="cameraShape1" name="cameraShape1">
-            <optics>
-                <technique_common>
-                    <perspective>
-                        <yfov>37.8493</yfov>
-                        <aspect_ratio>1.5</aspect_ratio>
-                        <znear>1</znear>
-                        <zfar>400</zfar>
-                    </perspective>
-                </technique_common>
-            </optics>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <film_fit>0</film_fit>
-                    <film_fit_offset>0</film_fit_offset>
-                    <film_offsetX>0</film_offsetX>
-                    <film_offsetY>0</film_offsetY>
-                    <horizontal_aperture>3.599993</horizontal_aperture>
-                    <lens_squeeze>1</lens_squeeze>
-                    <originalMayaNodeId>cameraShape1</originalMayaNodeId>
-                    <vertical_aperture>2.399995</vertical_aperture>
-                </technique>
-            </extra>
-        </camera>
-        <camera id="CameraDistShape" name="CameraDistShape">
-            <optics>
-                <technique_common>
-                    <perspective>
-                        <yfov>37.8493</yfov>
-                        <aspect_ratio>1.5</aspect_ratio>
-                        <znear>1</znear>
-                        <zfar>1000</zfar>
-                    </perspective>
-                </technique_common>
-            </optics>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <film_fit>0</film_fit>
-                    <film_fit_offset>0</film_fit_offset>
-                    <film_offsetX>0</film_offsetX>
-                    <film_offsetY>0</film_offsetY>
-                    <horizontal_aperture>3.599993</horizontal_aperture>
-                    <lens_squeeze>1</lens_squeeze>
-                    <originalMayaNodeId>CameraDistShape</originalMayaNodeId>
-                    <vertical_aperture>2.399995</vertical_aperture>
-                </technique>
-            </extra>
-        </camera>
-    </library_cameras>
-    <library_materials>
-        <material id="Paint1" name="Paint1">
-            <instance_effect url="#Paint1-fx" />
-        </material>
-        <material id="lambert2" name="lambert2">
-            <instance_effect url="#lambert2-fx" />
-        </material>
-        <material id="Plastic" name="Plastic">
-            <instance_effect url="#Plastic-fx" />
-        </material>
-        <material id="Metal" name="Metal">
-            <instance_effect url="#Metal-fx" />
-        </material>
-        <material id="PlasticCenter" name="PlasticCenter">
-            <instance_effect url="#PlasticCenter-fx" />
-        </material>
-        <material id="PlasticRed" name="PlasticRed">
-            <instance_effect url="#PlasticRed-fx" />
-        </material>
-        <material id="lambert10" name="lambert10">
-            <instance_effect url="#lambert10-fx" />
-        </material>
-        <material id="lambert11" name="lambert11">
-            <instance_effect url="#lambert11-fx" />
-        </material>
-    </library_materials>
-    <library_effects>
-        <effect id="Metal-fx">
-            <profile_COMMON>
-                <newparam sid="file23-surface">
-                    <surface type="2D">
-                        <init_from>file23</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file23-sampler">
-                    <sampler2D>
-                        <source>file23-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file23-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="Paint1-fx">
-            <profile_COMMON>
-                <newparam sid="file25-surface">
-                    <surface type="2D">
-                        <init_from>file25</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file25-sampler">
-                    <sampler2D>
-                        <source>file25-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file25-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="Plastic-fx">
-            <profile_COMMON>
-                <newparam sid="file24-surface">
-                    <surface type="2D">
-                        <init_from>file24</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file24-sampler">
-                    <sampler2D>
-                        <source>file24-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file24-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="PlasticCenter-fx">
-            <profile_COMMON>
-                <newparam sid="file24-surface">
-                    <surface type="2D">
-                        <init_from>file24</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file24-sampler">
-                    <sampler2D>
-                        <source>file24-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file24-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="PlasticRed-fx">
-            <profile_COMMON>
-                <newparam sid="file23-surface">
-                    <surface type="2D">
-                        <init_from>file23</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file23-sampler">
-                    <sampler2D>
-                        <source>file23-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file23-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="lambert10-fx">
-            <profile_COMMON>
-                <newparam sid="file28-surface">
-                    <surface type="2D">
-                        <init_from>file28</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file28-sampler">
-                    <sampler2D>
-                        <source>file28-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file28-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="lambert11-fx">
-            <profile_COMMON>
-                <newparam sid="file29-surface">
-                    <surface type="2D">
-                        <init_from>file29</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file29-sampler">
-                    <sampler2D>
-                        <source>file29-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file29-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-        <effect id="lambert2-fx">
-            <profile_COMMON>
-                <newparam sid="file22-surface">
-                    <surface type="2D">
-                        <init_from>file22</init_from>
-                    </surface>
-                </newparam>
-                <newparam sid="file22-sampler">
-                    <sampler2D>
-                        <source>file22-surface</source>
-                    </sampler2D>
-                </newparam>
-                <technique sid="common">
-                    <lambert>
-                        <emission>
-                            <color>0 0 0 1</color>
-                        </emission>
-                        <ambient>
-                            <color>0 0 0 1</color>
-                        </ambient>
-                        <diffuse>
-                            <texture texture="file22-sampler" texcoord="TEX0">
-                                <extra>
-                                    <technique profile="OpenCOLLADAMaya">
-                                        <blend_mode>NONE</blend_mode>
-                                        <coverageU>1</coverageU>
-                                        <coverageV>1</coverageV>
-                                        <fast>0</fast>
-                                        <mirrorU>0</mirrorU>
-                                        <mirrorV>0</mirrorV>
-                                        <noiseU>0</noiseU>
-                                        <noiseV>0</noiseV>
-                                        <offsetU>0</offsetU>
-                                        <offsetV>0</offsetV>
-                                        <repeatU>1</repeatU>
-                                        <repeatV>1</repeatV>
-                                        <rotateFrame>0</rotateFrame>
-                                        <rotateUV>0</rotateUV>
-                                        <stagger>0</stagger>
-                                        <translateFrameU>0</translateFrameU>
-                                        <translateFrameV>0</translateFrameV>
-                                        <wrapU>1</wrapU>
-                                        <wrapV>1</wrapV>
-                                    </technique>
-                                </extra>
-                            </texture>
-                        </diffuse>
-                        <transparent opaque="RGB_ZERO">
-                            <color>0 0 0 1</color>
-                        </transparent>
-                        <transparency>
-                            <float>1</float>
-                        </transparency>
-                    </lambert>
-                </technique>
-            </profile_COMMON>
-        </effect>
-    </library_effects>
-    <library_images>
-        <image id="file29" name="file29" height="0" width="0">
-            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/blue.jpg</init_from>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <dgnode_type>kFile</dgnode_type>
-                    <image_sequence>0</image_sequence>
-                    <originalMayaNodeId>file29</originalMayaNodeId>
-                </technique>
-            </extra>
-        </image>
-        <image id="file25" name="file25" height="0" width="0">
-            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/carbonfiber.jpg</init_from>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <dgnode_type>kFile</dgnode_type>
-                    <image_sequence>0</image_sequence>
-                    <originalMayaNodeId>file25</originalMayaNodeId>
-                </technique>
-            </extra>
-        </image>
-        <image id="file28" name="file28" height="0" width="0">
-            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/green.jpg</init_from>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <dgnode_type>kFile</dgnode_type>
-                    <image_sequence>0</image_sequence>
-                    <originalMayaNodeId>file28</originalMayaNodeId>
-                </technique>
-            </extra>
-        </image>
-        <image id="file22" name="file22" height="0" width="0">
-            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/grey.jpg</init_from>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <dgnode_type>kFile</dgnode_type>
-                    <image_sequence>0</image_sequence>
-                    <originalMayaNodeId>file22</originalMayaNodeId>
-                </technique>
-            </extra>
-        </image>
-        <image id="file24" name="file24" height="0" width="0">
-            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/orange.jpg</init_from>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <dgnode_type>kFile</dgnode_type>
-                    <image_sequence>0</image_sequence>
-                    <originalMayaNodeId>file24</originalMayaNodeId>
-                </technique>
-            </extra>
-        </image>
-        <image id="file23" name="file23" height="0" width="0">
-            <init_from>file:///Volumes/Android/Sanity/SceneGraph/assets/red.jpg</init_from>
-            <extra>
-                <technique profile="OpenCOLLADAMaya">
-                    <dgnode_type>kFile</dgnode_type>
-                    <image_sequence>0</image_sequence>
-                    <originalMayaNodeId>file23</originalMayaNodeId>
-                </technique>
-            </extra>
-        </image>
-    </library_images>
-    <library_visual_scenes>
-        <visual_scene id="VisualSceneNode" name="orientation_test">
-            <node id="camera1" name="camera1">
-                <translate sid="translate">24.5791 14.1321 31.4654</translate>
-                <rotate sid="rotateZ">0 0 1 0</rotate>
-                <rotate sid="rotateY">0 1 0 42</rotate>
-                <rotate sid="rotateX">1 0 0 -16.2</rotate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_camera url="#cameraShape1" />
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>camera1</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="CameraAim" name="CameraAim">
-                <translate sid="translate">0.0209301 3.68542 2.06912</translate>
-                <rotate sid="rotateY">0 1 0 43.2561</rotate>
-                <rotate sid="rotateX">1 0 0 -20</rotate>
-                <scale sid="scale">1 1 1</scale>
-                <node id="CameraDist" name="CameraDist">
-                    <translate sid="translate">0 0 45</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_camera url="#CameraDistShape" />
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>CameraDist</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>CameraAim</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pSphere4" name="pSphere4">
-                <translate sid="translate">-9.69237 0 7.70498</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pSphereShape4">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert7SG" target="#Paint1">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pSphere4</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pSphere1" name="pSphere1">
-                <translate sid="translate">13.0966 0 5.76254</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pSphereShape1">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert7SG" target="#Paint1">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pSphere1</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pSphere2" name="pSphere2">
-                <translate sid="translate">21.7661 0 -13.6375</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pSphereShape2">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert7SG" target="#Paint1">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pSphere2</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pSphere3" name="pSphere3">
-                <translate sid="translate">-13.862 0 -13.6154</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pSphereShape3">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert7SG" target="#Paint1">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pSphere3</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pSphere5" name="pSphere5">
-                <translate sid="translate">31.0862 0 18.5992</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pSphereShape5">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert7SG" target="#Paint1">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pSphere5</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pCube1" name="pCube1">
-                <translate sid="translate">0 0 0</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pCubeShape1">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert4SG" target="#lambert2">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pCube1</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="group1" name="group1">
-                <translate sid="translate">0 0 0</translate>
-                <rotate sid="rotateZ">0 0 1 -162.693</rotate>
-                <rotate sid="rotateY">0 1 0 21.3345</rotate>
-                <rotate sid="rotateX">1 0 0 -100.567</rotate>
-                <scale sid="scale">1 1 1</scale>
-                <node id="pSphere6" name="pSphere6">
-                    <translate sid="translate">-13.862 0 -13.6154</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape6">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert6SG" target="#Plastic">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere6</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere7" name="pSphere7">
-                    <translate sid="translate">-9.69237 0 7.70498</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape7">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert6SG" target="#Plastic">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere7</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere8" name="pSphere8">
-                    <translate sid="translate">21.7661 0 -13.6375</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape8">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert6SG" target="#Plastic">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere8</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere9" name="pSphere9">
-                    <translate sid="translate">13.0966 0 5.76254</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape9">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert6SG" target="#Plastic">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere9</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>group1</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="group2" name="group2">
-                <translate sid="translate">0 0 0</translate>
-                <rotate sid="rotateZ">0 0 1 45.4017</rotate>
-                <rotate sid="rotateY">0 1 0 79.393</rotate>
-                <rotate sid="rotateX">1 0 0 5.10889</rotate>
-                <scale sid="scale">1 1 1</scale>
-                <node id="pSphere10" name="pSphere10">
-                    <translate sid="translate">31.0862 0 18.5992</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape10">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere10</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere11" name="pSphere11">
-                    <translate sid="translate">13.0966 0 5.76254</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape11">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere11</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere12" name="pSphere12">
-                    <translate sid="translate">7.4784 16.3496 7.36882</translate>
-                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
-                    <rotate sid="rotateY">0 1 0 158.666</rotate>
-                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape12">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere12</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere13" name="pSphere13">
-                    <translate sid="translate">-9.69237 0 7.70498</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape13">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere13</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere14" name="pSphere14">
-                    <translate sid="translate">11.3635 -4.3926 2.21012</translate>
-                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
-                    <rotate sid="rotateY">0 1 0 158.666</rotate>
-                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape14">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere14</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere15" name="pSphere15">
-                    <translate sid="translate">21.7661 0 -13.6375</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape15">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere15</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere16" name="pSphere16">
-                    <translate sid="translate">-9.5945 -8.92317 -5.74901</translate>
-                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
-                    <rotate sid="rotateY">0 1 0 158.666</rotate>
-                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape16">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere16</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere17" name="pSphere17">
-                    <translate sid="translate">-13.862 0 -13.6154</translate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape17">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere17</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <node id="pSphere18" name="pSphere18">
-                    <translate sid="translate">-24.2135 6.497 -5.58935</translate>
-                    <rotate sid="rotateZ">0 0 1 17.3073</rotate>
-                    <rotate sid="rotateY">0 1 0 158.666</rotate>
-                    <rotate sid="rotateX">1 0 0 79.4335</rotate>
-                    <scale sid="scale">1 1 1</scale>
-                    <instance_geometry url="#pSphereShape18">
-                        <bind_material>
-                            <technique_common>
-                                <instance_material symbol="lambert5SG" target="#Metal">
-                                    <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                                </instance_material>
-                            </technique_common>
-                        </bind_material>
-                    </instance_geometry>
-                    <extra>
-                        <technique profile="OpenCOLLADAMaya">
-                            <originalMayaNodeId>pSphere18</originalMayaNodeId>
-                        </technique>
-                    </extra>
-                </node>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>group2</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pCube2" name="pCube2">
-                <translate sid="translate">0 0 0</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pCubeShape2">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert8SG" target="#PlasticCenter">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pCube2</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pCube3" name="pCube3">
-                <translate sid="translate">15 0 0</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pCubeShape3">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert9SG" target="#PlasticRed">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pCube3</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pCube4" name="pCube4">
-                <translate sid="translate">0 15 0</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pCubeShape4">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert10SG" target="#lambert10">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pCube4</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-            <node id="pCube5" name="pCube5">
-                <translate sid="translate">0 0 15</translate>
-                <scale sid="scale">1 1 1</scale>
-                <instance_geometry url="#pCubeShape5">
-                    <bind_material>
-                        <technique_common>
-                            <instance_material symbol="lambert11SG" target="#lambert11">
-                                <bind_vertex_input semantic="TEX0" input_semantic="TEXCOORD" input_set="0" />
-                            </instance_material>
-                        </technique_common>
-                    </bind_material>
-                </instance_geometry>
-                <extra>
-                    <technique profile="OpenCOLLADAMaya">
-                        <originalMayaNodeId>pCube5</originalMayaNodeId>
-                    </technique>
-                </extra>
-            </node>
-        </visual_scene>
-    </library_visual_scenes>
-    <scene>
-        <instance_visual_scene url="#VisualSceneNode" />
-    </scene>
-</COLLADA>
diff --git a/tests/RenderScriptTests/SceneGraph/assets/paint.jpg b/tests/RenderScriptTests/SceneGraph/assets/paint.jpg
deleted file mode 100644
index 0791045..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/paint.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/assets/red.jpg b/tests/RenderScriptTests/SceneGraph/assets/red.jpg
deleted file mode 100644
index 320a2a6..0000000
--- a/tests/RenderScriptTests/SceneGraph/assets/red.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png
deleted file mode 100644
index ff34a7f..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png
deleted file mode 100644
index f7353fd..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/robot.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml b/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml
deleted file mode 100644
index 9ea30107..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/menu/loader_menu.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/load_model"
-          android:title="@string/load_model" />
-    <item android:id="@+id/use_blur"
-          android:title="@string/use_blur" />
-</menu>
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
deleted file mode 100644
index c34adc9..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl
+++ /dev/null
@@ -1,15 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   vec2 blurCoord = varTex0;
-   blurCoord.x = varTex0.x + UNI_blurOffset0;
-   vec3 col = texture2D(UNI_color, blurCoord).rgb;
-   blurCoord.x = varTex0.x + UNI_blurOffset1;
-   col += texture2D(UNI_color, blurCoord).rgb;
-   blurCoord.x = varTex0.x + UNI_blurOffset2;
-   col += texture2D(UNI_color, blurCoord).rgb;
-   blurCoord.x = varTex0.x + UNI_blurOffset3;
-   col += texture2D(UNI_color, blurCoord).rgb;
-
-   gl_FragColor = vec4(col * 0.25, 0.0);
-}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
deleted file mode 100644
index ade05a2..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl
+++ /dev/null
@@ -1,17 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   vec2 blurCoord = varTex0;
-   blurCoord.y = varTex0.y + UNI_blurOffset0;
-   vec3 col = texture2D(UNI_color, blurCoord).rgb;
-   blurCoord.y = varTex0.y + UNI_blurOffset1;
-   col += texture2D(UNI_color, blurCoord).rgb;
-   blurCoord.y = varTex0.y + UNI_blurOffset2;
-   col += texture2D(UNI_color, blurCoord).rgb;
-   blurCoord.y = varTex0.y + UNI_blurOffset3;
-   col += texture2D(UNI_color, blurCoord).rgb;
-
-   col = col * 0.25;
-
-   gl_FragColor = vec4(col, 0.0);
-}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl
deleted file mode 100644
index bc824b6..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_vertex.glsl
+++ /dev/null
@@ -1,7 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   gl_Position = ATTRIB_position;
-   varTex0 = ATTRIB_texture0;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
deleted file mode 100644
index 2eb1028..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl
+++ /dev/null
@@ -1,19 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
-   vec3 worldNorm = (varWorldNormal);
-
-   vec3 light0Vec = V;
-   vec3 light0R = reflect(light0Vec, worldNorm);
-   float light0_Diffuse = dot(worldNorm, light0Vec);
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
-   col.xyz = col.xyz * light0_Diffuse * 1.2;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse_lights.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse_lights.glsl
deleted file mode 100644
index ef93e1c..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse_lights.glsl
+++ /dev/null
@@ -1,22 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = normalize(UNI_lightPos_0.xyz - varWorldPos.xyz);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
-
-   vec3 light1Vec = normalize(UNI_lightPos_1.xyz - varWorldPos.xyz);
-   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0);
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = UNI_diffuse;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_lightColor_0.xyz +
-                        light1_Diffuse * UNI_lightColor_1.xyz);
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
deleted file mode 100644
index b90a7b2..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl
+++ /dev/null
@@ -1,23 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = V;
-   vec3 light0R = reflect(light0Vec, worldNorm);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
-   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
-   float light0_Specular = pow(light0Spec, 15.0) * 0.5;
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
-   col.xyz = col.xyz * (textureCube(UNI_reflection, worldNorm).rgb * 0.5 + vec3(light0_Diffuse));
-   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
-
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
deleted file mode 100644
index f3b89ed..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl
+++ /dev/null
@@ -1,26 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = V;
-   vec3 light0R = reflect(light0Vec, worldNorm);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.01, 0.99);
-   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
-   float light0_Specular = pow(light0Spec, 150.0) * 0.5;
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
-   col.xyz = col.xyz * light0_Diffuse * 1.1;
-   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
-
-   float fresnel = mix(pow(1.0 - light0_Diffuse, 15.0), 1.0, 0.1);
-   col.xyz = mix(col.xyz, textureCube(UNI_reflection, -light0R).rgb * 2.4, fresnel);
-   col.w = 0.8;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
deleted file mode 100644
index 56f7151f..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl
+++ /dev/null
@@ -1,22 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = V;
-   vec3 light0R = reflect(light0Vec, worldNorm);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
-   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
-   float light0_Specular = pow(light0Spec, 10.0) * 0.5;
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = texture2D(UNI_diffuse, t0).rgba;
-   col.xyz = col.xyz * light0_Diffuse * 1.2;
-   col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0);
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl
deleted file mode 100644
index b253622..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/plastic_lights.glsl
+++ /dev/null
@@ -1,29 +0,0 @@
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-void main() {
-
-   vec3 V = normalize(UNI_cameraPos.xyz - varWorldPos.xyz);
-   vec3 worldNorm = normalize(varWorldNormal);
-
-   vec3 light0Vec = normalize(UNI_lightPos_0.xyz - varWorldPos.xyz);
-   vec3 light0R = reflect(light0Vec, worldNorm);
-   float light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0);
-   float light0Spec = clamp(dot(-light0R, V), 0.001, 1.0);
-   float light0_Specular = pow(light0Spec, 10.0) * 0.7;
-
-   vec3 light1Vec = normalize(UNI_lightPos_1.xyz - varWorldPos.xyz);
-   vec3 light1R = reflect(light1Vec, worldNorm);
-   float light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0);
-   float light1Spec = clamp(dot(-light1R, V), 0.001, 1.0);
-   float light1_Specular = pow(light1Spec, 10.0) * 0.7;
-
-   vec2 t0 = varTex0.xy;
-   lowp vec4 col = UNI_diffuse;
-   col.xyz = col.xyz * (light0_Diffuse * UNI_lightColor_0.xyz +
-                        light1_Diffuse * UNI_lightColor_1.xyz);
-   col.xyz += (light0_Specular + light1_Specular);
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d b/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d
deleted file mode 100644
index f48895c..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/robot.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
deleted file mode 100644
index 1a927ca..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   vec3 col = texture2D(UNI_color, varTex0).rgb;
-
-   vec3 desat = vec3(0.299, 0.587, 0.114);
-   float lum = dot(desat, col);
-   float stepVal = step(lum, 0.8);
-   col = mix(col, vec3(0.0), stepVal)*0.5;
-
-   gl_FragColor = vec4(col, 0.0);
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl
deleted file mode 100644
index 7910a54..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/shader2v.glsl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
-    rs_matrix4x4 model;
-    rs_matrix4x4 viewProj;
-*/
-
-varying vec3 varWorldPos;
-varying vec3 varWorldNormal;
-varying vec2 varTex0;
-
-// This is where actual shader code begins
-void main() {
-   vec4 objPos = ATTRIB_position;
-   vec4 worldPos = UNI_model * objPos;
-   gl_Position = UNI_viewProj * worldPos;
-
-   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
-   vec3 worldNorm = model3 * ATTRIB_normal;
-
-   varWorldPos = worldPos.xyz;
-   varWorldNormal = worldNorm;
-   varTex0 = ATTRIB_texture0;
-}
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
deleted file mode 100644
index 662ecd8..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl
+++ /dev/null
@@ -1,7 +0,0 @@
-varying vec2 varTex0;
-
-void main() {
-   lowp vec4 col = texture2D(UNI_color, varTex0).rgba;
-   gl_FragColor = col;
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/unit_obj.a3d b/tests/RenderScriptTests/SceneGraph/res/raw/unit_obj.a3d
deleted file mode 100644
index 56eff04..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/raw/unit_obj.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/SceneGraph/res/values/strings.xml b/tests/RenderScriptTests/SceneGraph/res/values/strings.xml
deleted file mode 100644
index c916d79..0000000
--- a/tests/RenderScriptTests/SceneGraph/res/values/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2011 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <skip />
-    <string name="load_model">Load Model</string>
-    <string name="use_blur">Use Blur</string>
-</resources>
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java
deleted file mode 100644
index 42f2be5..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Camera.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import com.android.scenegraph.SceneManager;
-
-import android.renderscript.*;
-import android.renderscript.Matrix4f;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class Camera extends SceneGraphBase {
-
-    Transform mTransform;
-
-    ScriptField_Camera_s.Item mData;
-    ScriptField_Camera_s mField;
-
-    public Camera() {
-        mData = new ScriptField_Camera_s.Item();
-        mData.near = 0.1f;
-        mData.far = 1000.0f;
-        mData.horizontalFOV = 60.0f;
-        mData.aspect = 0;
-    }
-
-    public void setTransform(Transform t) {
-        mTransform = t;
-        if (mField != null) {
-            mField.set_transformMatrix(0, mTransform.getRSData().getAllocation(), true);
-            mField.set_isDirty(0, 1, true);
-        }
-    }
-    public void setFOV(float fov) {
-        mData.horizontalFOV = fov;
-        if (mField != null) {
-            mField.set_horizontalFOV(0, fov, true);
-            mField.set_isDirty(0, 1, true);
-        }
-    }
-
-    public void setNear(float n) {
-        mData.near = n;
-        if (mField != null) {
-            mField.set_near(0, n, true);
-            mField.set_isDirty(0, 1, true);
-        }
-    }
-
-    public void setFar(float f) {
-        mData.far = f;
-        if (mField != null) {
-            mField.set_far(0, f, true);
-            mField.set_isDirty(0, 1, true);
-        }
-    }
-
-    public void setName(String n) {
-        super.setName(n);
-        if (mField != null) {
-            RenderScriptGL rs = SceneManager.getRS();
-            mData.name = getNameAlloc(rs);
-            mField.set_name(0, mData.name, true);
-            mField.set_isDirty(0, 1, true);
-        }
-    }
-
-    ScriptField_Camera_s getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        if (rs == null) {
-            return null;
-        }
-
-        if (mTransform == null) {
-            throw new RuntimeException("Cameras without transforms are invalid");
-        }
-
-        mField = new ScriptField_Camera_s(rs, 1);
-
-        mData.transformMatrix = mTransform.getRSData().getAllocation();
-        mData.transformTimestamp = 1;
-        mData.timestamp = 1;
-        mData.isDirty = 1;
-        mData.name = getNameAlloc(rs);
-        mField.set(mData, 0, true);
-
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
deleted file mode 100644
index b4b6fb9..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java
+++ /dev/null
@@ -1,563 +0,0 @@
-/*

- * Copyright (C) 2011 The Android Open Source Project

- *

- * Licensed under the Apache License, Version 2.0 (the "License");

- * you may not use this file except in compliance with the License.

- * You may obtain a copy of the License at

- *

- *      http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing, software

- * distributed under the License is distributed on an "AS IS" BASIS,

- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

- * See the License for the specific language governing permissions and

- * limitations under the License.

- */

-

-package com.android.scenegraph;

-import com.android.scenegraph.CompoundTransform.TranslateComponent;

-import com.android.scenegraph.CompoundTransform.RotateComponent;

-import com.android.scenegraph.CompoundTransform.ScaleComponent;

-import java.io.IOException;

-import java.io.InputStream;

-import java.util.ArrayList;

-import java.util.Iterator;

-import java.util.List;

-import java.util.StringTokenizer;

-import java.util.HashMap;

-

-import javax.xml.parsers.DocumentBuilder;

-import javax.xml.parsers.DocumentBuilderFactory;

-import javax.xml.parsers.ParserConfigurationException;

-

-import org.w3c.dom.Document;

-import org.w3c.dom.Element;

-import org.w3c.dom.Node;

-import org.w3c.dom.NodeList;

-import org.xml.sax.SAXException;

-

-import android.renderscript.*;

-import android.util.Log;

-

-public class ColladaParser {

-    static final String TAG = "ColladaParser";

-    Document mDom;

-

-    HashMap<String, LightBase> mLights;

-    HashMap<String, Camera> mCameras;

-    HashMap<String, ArrayList<ShaderParam> > mEffectsParams;

-    HashMap<String, Texture2D> mImages;

-    HashMap<String, Texture2D> mSamplerImageMap;

-    HashMap<String, String> mMeshIdNameMap;

-    Scene mScene;

-

-    String mRootDir;

-

-    String toString(Float3 v) {

-        String valueStr = v.x + " " + v.y + " " + v.z;

-        return valueStr;

-    }

-

-    String toString(Float4 v) {

-        String valueStr = v.x + " " + v.y + " " + v.z + " " + v.w;

-        return valueStr;

-    }

-

-    public ColladaParser(){

-        mLights = new HashMap<String, LightBase>();

-        mCameras = new HashMap<String, Camera>();

-        mEffectsParams = new HashMap<String, ArrayList<ShaderParam> >();

-        mImages = new HashMap<String, Texture2D>();

-        mMeshIdNameMap = new HashMap<String, String>();

-    }

-

-    public void init(InputStream is, String rootDir) {

-        mLights.clear();

-        mCameras.clear();

-        mEffectsParams.clear();

-

-        mRootDir = rootDir;

-

-        long start = System.currentTimeMillis();

-        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

-        try {

-            DocumentBuilder db = dbf.newDocumentBuilder();

-            mDom = db.parse(is);

-        } catch(ParserConfigurationException e) {

-            e.printStackTrace();

-        } catch(SAXException e) {

-            e.printStackTrace();

-        } catch(IOException e) {

-            e.printStackTrace();

-        }

-        long end = System.currentTimeMillis();

-        Log.v("TIMER", "    Parse time: " + (end - start));

-        exportSceneData();

-    }

-

-    Scene getScene() {

-        return mScene;

-    }

-

-    private void exportSceneData(){

-        mScene = new Scene();

-

-        Element docEle = mDom.getDocumentElement();

-        NodeList nl = docEle.getElementsByTagName("light");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element l = (Element)nl.item(i);

-                convertLight(l);

-            }

-        }

-

-        nl = docEle.getElementsByTagName("camera");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element c = (Element)nl.item(i);

-                convertCamera(c);

-            }

-        }

-

-        nl = docEle.getElementsByTagName("image");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element img = (Element)nl.item(i);

-                convertImage(img);

-            }

-        }

-

-        nl = docEle.getElementsByTagName("effect");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element e = (Element)nl.item(i);

-                convertEffects(e);

-            }

-        }

-

-        // Material is just a link to the effect

-        nl = docEle.getElementsByTagName("material");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element m = (Element)nl.item(i);

-                convertMaterials(m);

-            }

-        }

-

-        // Look through the geometry list and build up a correlation between id's and names

-        nl = docEle.getElementsByTagName("geometry");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element m = (Element)nl.item(i);

-                convertGeometries(m);

-            }

-        }

-

-

-        nl = docEle.getElementsByTagName("visual_scene");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element s = (Element)nl.item(i);

-                getScene(s);

-            }

-        }

-    }

-

-    private void getRenderable(Element shape, Transform t) {

-        String geoURL = shape.getAttribute("url").substring(1);

-        String geoName = mMeshIdNameMap.get(geoURL);

-        if (geoName != null) {

-            geoURL = geoName;

-        }

-        //RenderableGroup group = new RenderableGroup();

-        //group.setName(geoURL.substring(1));

-        //mScene.appendRenderable(group);

-        NodeList nl = shape.getElementsByTagName("instance_material");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element materialRef = (Element)nl.item(i);

-                String meshIndexName = materialRef.getAttribute("symbol");

-                String materialName = materialRef.getAttribute("target");

-

-                Renderable d = new Renderable();

-                d.setMesh(geoURL, meshIndexName);

-                d.setMaterialName(materialName.substring(1));

-                d.setName(geoURL);

-

-                //Log.v(TAG, "Created drawable geo " + geoURL + " index " + meshIndexName + " material " + materialName);

-

-                d.setTransform(t);

-                //Log.v(TAG, "Set source param " + t.getName());

-

-                // Now find all the parameters that exist on the material

-                ArrayList<ShaderParam> materialParams;

-                materialParams = mEffectsParams.get(materialName.substring(1));

-                for (int pI = 0; pI < materialParams.size(); pI ++) {

-                    d.appendSourceParams(materialParams.get(pI));

-                    //Log.v(TAG, "Set source param i: " + pI + " name " + materialParams.get(pI).getParamName());

-                }

-                mScene.appendRenderable(d);

-                //group.appendChildren(d);

-            }

-        }

-    }

-

-    private void updateLight(Element shape, Transform t) {

-        String lightURL = shape.getAttribute("url");

-        // collada uses a uri structure to link things,

-        // but we ignore it for now and do a simple search

-        LightBase light = mLights.get(lightURL.substring(1));

-        if (light != null) {

-            light.setTransform(t);

-            //Log.v(TAG, "Set Light " + light.getName() + " " + t.getName());

-        }

-    }

-

-    private void updateCamera(Element shape, Transform t) {

-        String camURL = shape.getAttribute("url");

-        // collada uses a uri structure to link things,

-        // but we ignore it for now and do a simple search

-        Camera cam = mCameras.get(camURL.substring(1));

-        if (cam != null) {

-            cam.setTransform(t);

-            //Log.v(TAG, "Set Camera " + cam.getName() + " " + t.getName());

-        }

-    }

-

-    private void getNode(Element node, Transform parent, String indent) {

-        String name = node.getAttribute("name");

-        String id = node.getAttribute("id");

-        CompoundTransform current = new CompoundTransform();

-        current.setName(name);

-        if (parent != null) {

-            parent.appendChild(current);

-        } else {

-            mScene.appendTransform(current);

-        }

-

-        mScene.addToTransformMap(current);

-

-        //Log.v(TAG, indent + "|");

-        //Log.v(TAG, indent + "[" + name + "]");

-

-        Node childNode = node.getFirstChild();

-        while (childNode != null) {

-            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

-                Element field = (Element)childNode;

-                String fieldName = field.getTagName();

-                String description = field.getAttribute("sid");

-                if (fieldName.equals("translate")) {

-                    Float3 value = getFloat3(field);

-                    current.addTranslate(description, value);

-                    //Log.v(TAG, indent + " translate " + description + toString(value));

-                } else if (fieldName.equals("rotate")) {

-                    Float4 value = getFloat4(field);

-                    //Log.v(TAG, indent + " rotate " + description + toString(value));

-                    Float3 axis = new Float3(value.x, value.y, value.z);

-                    current.addRotate(description, axis, value.w);

-                } else if (fieldName.equals("scale")) {

-                    Float3 value = getFloat3(field);

-                    //Log.v(TAG, indent + " scale " + description + toString(value));

-                    current.addScale(description, value);

-                } else if (fieldName.equals("instance_geometry")) {

-                    getRenderable(field, current);

-                } else if (fieldName.equals("instance_light")) {

-                    updateLight(field, current);

-                } else if (fieldName.equals("instance_camera")) {

-                    updateCamera(field, current);

-                } else if (fieldName.equals("node")) {

-                    getNode(field, current, indent + "   ");

-                }

-            }

-            childNode = childNode.getNextSibling();

-        }

-    }

-

-    // This will find the actual texture node, which is sometimes hidden behind a sampler

-    // and sometimes referenced directly

-    Texture2D getTexture(String samplerName) {

-        String texName = samplerName;

-

-        // Check to see if the image file is hidden by a sampler surface link combo

-        Element sampler = mDom.getElementById(samplerName);

-        if (sampler != null) {

-            NodeList nl = sampler.getElementsByTagName("source");

-            if (nl != null && nl.getLength() == 1) {

-                Element ref = (Element)nl.item(0);

-                String surfaceName = getString(ref);

-                if (surfaceName == null) {

-                    return null;

-                }

-

-                Element surface = mDom.getElementById(surfaceName);

-                if (surface == null) {

-                    return null;

-                }

-                nl = surface.getElementsByTagName("init_from");

-                if (nl != null && nl.getLength() == 1) {

-                    ref = (Element)nl.item(0);

-                    texName = getString(ref);

-                }

-            }

-        }

-

-        //Log.v(TAG, "Extracted texture name " + texName);

-        return mImages.get(texName);

-    }

-

-    void extractParams(Element fx, ArrayList<ShaderParam> params) {

-        Node paramNode = fx.getFirstChild();

-        while (paramNode != null) {

-            if (paramNode.getNodeType() == Node.ELEMENT_NODE) {

-                String name = paramNode.getNodeName();

-                // Now find what type it is

-                Node typeNode = paramNode.getFirstChild();

-                while (typeNode != null && typeNode.getNodeType() != Node.ELEMENT_NODE) {

-                    typeNode = typeNode.getNextSibling();

-                }

-                String paramType = typeNode.getNodeName();

-                Element typeElem = (Element)typeNode;

-                ShaderParam sceneParam = null;

-                if (paramType.equals("color")) {

-                    Float4Param f4p = new Float4Param(name);

-                    Float4 value = getFloat4(typeElem);

-                    f4p.setValue(value);

-                    sceneParam = f4p;

-                    //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + toString(value));

-                } else if (paramType.equals("float")) {

-                    Float4Param f4p = new Float4Param(name);

-                    float value = getFloat(typeElem);

-                    f4p.setValue(new Float4(value, value, value, value));

-                    sceneParam = f4p;

-                    //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + value);

-                }  else if (paramType.equals("texture")) {

-                    String samplerName = typeElem.getAttribute("texture");

-                    Texture2D tex = getTexture(samplerName);

-                    TextureParam texP = new TextureParam(name);

-                    texP.setTexture(tex);

-                    sceneParam = texP;

-                    //Log.v(TAG, "Extracted texture " + tex);

-                }

-                if (sceneParam != null) {

-                    params.add(sceneParam);

-                }

-            }

-            paramNode = paramNode.getNextSibling();

-        }

-    }

-

-    private void convertMaterials(Element mat) {

-        String id = mat.getAttribute("id");

-        NodeList nl = mat.getElementsByTagName("instance_effect");

-        if (nl != null && nl.getLength() == 1) {

-            Element ref = (Element)nl.item(0);

-            String url = ref.getAttribute("url");

-            ArrayList<ShaderParam> params = mEffectsParams.get(url.substring(1));

-            mEffectsParams.put(id, params);

-        }

-    }

-

-    private void convertGeometries(Element geo) {

-        String id = geo.getAttribute("id");

-        String name = geo.getAttribute("name");

-        if (!id.equals(name)) {

-            mMeshIdNameMap.put(id, name);

-        }

-    }

-

-    private void convertEffects(Element fx) {

-        String id = fx.getAttribute("id");

-        ArrayList<ShaderParam> params = new ArrayList<ShaderParam>();

-

-        NodeList nl = fx.getElementsByTagName("newparam");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element field = (Element)nl.item(i);

-                field.setIdAttribute("sid", true);

-            }

-        }

-

-        nl = fx.getElementsByTagName("blinn");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element field = (Element)nl.item(i);

-                //Log.v(TAG, "blinn");

-                extractParams(field, params);

-            }

-        }

-        nl = fx.getElementsByTagName("lambert");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element field = (Element)nl.item(i);

-                //Log.v(TAG, "lambert");

-                extractParams(field, params);

-            }

-        }

-        nl = fx.getElementsByTagName("phong");

-        if (nl != null) {

-            for(int i = 0; i < nl.getLength(); i++) {

-                Element field = (Element)nl.item(i);

-                //Log.v(TAG, "phong");

-                extractParams(field, params);

-            }

-        }

-        mEffectsParams.put(id, params);

-    }

-

-    private void convertLight(Element light) {

-        String name = light.getAttribute("name");

-        String id = light.getAttribute("id");

-

-        // Determine type

-        String[] knownTypes = { "point", "spot", "directional" };

-        final int POINT_LIGHT = 0;

-        final int SPOT_LIGHT = 1;

-        final int DIR_LIGHT = 2;

-        int type = -1;

-        for (int i = 0; i < knownTypes.length; i ++) {

-            NodeList nl = light.getElementsByTagName(knownTypes[i]);

-            if (nl != null && nl.getLength() != 0) {

-                type = i;

-                break;

-            }

-        }

-

-        //Log.v(TAG, "Found Light Type " + type);

-

-        LightBase sceneLight = null;

-        switch (type) {

-        case POINT_LIGHT:

-            sceneLight = new PointLight();

-            break;

-        case SPOT_LIGHT: // TODO: finish light types

-            break;

-        case DIR_LIGHT: // TODO: finish light types

-            break;

-        }

-

-        if (sceneLight == null) {

-            return;

-        }

-

-        Float3 color = getFloat3(light, "color");

-        sceneLight.setColor(color.x, color.y, color.z);

-        sceneLight.setName(name);

-        mScene.appendLight(sceneLight);

-        mLights.put(id, sceneLight);

-

-        //Log.v(TAG, "Light " + name + " color " + toString(color));

-    }

-

-    private void convertCamera(Element camera) {

-        String name = camera.getAttribute("name");

-        String id = camera.getAttribute("id");

-        float fov = 30.0f;

-        if (getString(camera, "yfov") != null) {

-            fov = getFloat(camera, "yfov");

-        } else if(getString(camera, "xfov") != null) {

-            float aspect = getFloat(camera, "aspect_ratio");

-            fov = getFloat(camera, "xfov") / aspect;

-        }

-

-        float near = getFloat(camera, "znear");

-        float far = getFloat(camera, "zfar");

-

-        Camera sceneCamera = new Camera();

-        sceneCamera.setFOV(fov);

-        sceneCamera.setNear(near);

-        sceneCamera.setFar(far);

-        sceneCamera.setName(name);

-        mScene.appendCamera(sceneCamera);

-        mCameras.put(id, sceneCamera);

-    }

-

-    private void convertImage(Element img) {

-        String name = img.getAttribute("name");

-        String id = img.getAttribute("id");

-        String file = getString(img, "init_from");

-

-        Texture2D tex = new Texture2D();

-        tex.setFileName(file);

-        tex.setFileDir(mRootDir);

-        mScene.appendTextures(tex);

-        mImages.put(id, tex);

-    }

-

-    private void getScene(Element scene) {

-        String name = scene.getAttribute("name");

-        String id = scene.getAttribute("id");

-

-        Node childNode = scene.getFirstChild();

-        while (childNode != null) {

-            if (childNode.getNodeType() == Node.ELEMENT_NODE) {

-                String indent = "";

-                getNode((Element)childNode, null, indent);

-            }

-            childNode = childNode.getNextSibling();

-        }

-    }

-

-    private String getString(Element elem, String name) {

-        String text = null;

-        NodeList nl = elem.getElementsByTagName(name);

-        if (nl != null && nl.getLength() != 0) {

-            text = ((Element)nl.item(0)).getFirstChild().getNodeValue();

-        }

-        return text;

-    }

-

-    private String getString(Element elem) {

-        String text = null;

-        text = elem.getFirstChild().getNodeValue();

-        return text;

-    }

-

-    private int getInt(Element elem, String name) {

-        return Integer.parseInt(getString(elem, name));

-    }

-

-    private float getFloat(Element elem, String name) {

-        return Float.parseFloat(getString(elem, name));

-    }

-

-    private float getFloat(Element elem) {

-        return Float.parseFloat(getString(elem));

-    }

-

-    private Float3 parseFloat3(String valueString) {

-        StringTokenizer st = new StringTokenizer(valueString);

-        float x = Float.parseFloat(st.nextToken());

-        float y = Float.parseFloat(st.nextToken());

-        float z = Float.parseFloat(st.nextToken());

-        return new Float3(x, y, z);

-    }

-

-    private Float4 parseFloat4(String valueString) {

-        StringTokenizer st = new StringTokenizer(valueString);

-        float x = Float.parseFloat(st.nextToken());

-        float y = Float.parseFloat(st.nextToken());

-        float z = Float.parseFloat(st.nextToken());

-        float w = Float.parseFloat(st.nextToken());

-        return new Float4(x, y, z, w);

-    }

-

-    private Float3 getFloat3(Element elem, String name) {

-        String valueString = getString(elem, name);

-        return parseFloat3(valueString);

-    }

-

-    private Float4 getFloat4(Element elem, String name) {

-        String valueString = getString(elem, name);

-        return parseFloat4(valueString);

-    }

-

-    private Float3 getFloat3(Element elem) {

-        String valueString = getString(elem);

-        return parseFloat3(valueString);

-    }

-

-    private Float4 getFloat4(Element elem) {

-        String valueString = getString(elem);

-        return parseFloat4(valueString);

-    }

-}

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java
deleted file mode 100644
index 301075e..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaScene.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.FileInputStream;
-import java.io.BufferedInputStream;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
-import android.renderscript.*;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Element.Builder;
-import android.renderscript.Font.Style;
-import android.renderscript.Program.TextureType;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.util.Log;
-import com.android.scenegraph.SceneManager.SceneLoadedCallback;
-
-
-public class ColladaScene {
-
-    private String modelName;
-    private static String TAG = "ColladaScene";
-    private final int STATE_LAST_FOCUS = 1;
-    boolean mLoadFromSD = false;
-
-    SceneLoadedCallback mCallback;
-
-    public ColladaScene(String name, SceneLoadedCallback cb) {
-        modelName = name;
-        mCallback = cb;
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-
-        mLoadFromSD = SceneManager.isSDCardPath(modelName);
-
-        new ColladaLoaderTask().execute(modelName);
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    Scene mActiveScene;
-
-    private class ColladaLoaderTask extends AsyncTask<String, Void, Boolean> {
-        ColladaParser sceneSource;
-        protected Boolean doInBackground(String... names) {
-            String rootDir = names[0].substring(0, names[0].lastIndexOf('/') + 1);
-            long start = System.currentTimeMillis();
-            sceneSource = new ColladaParser();
-            InputStream is = null;
-            try {
-                if (!mLoadFromSD) {
-                    is = mRes.getAssets().open(names[0]);
-                } else {
-                    File f = new File(names[0]);
-                    is = new BufferedInputStream(new FileInputStream(f));
-                }
-            } catch (IOException e) {
-                Log.e(TAG, "Could not open collada file");
-                return new Boolean(false);
-            }
-            long end = System.currentTimeMillis();
-            Log.v("TIMER", "Stream load time: " + (end - start));
-
-            start = System.currentTimeMillis();
-            sceneSource.init(is, rootDir);
-            end = System.currentTimeMillis();
-            Log.v("TIMER", "Collada parse time: " + (end - start));
-            return new Boolean(true);
-        }
-
-        protected void onPostExecute(Boolean result) {
-            mActiveScene = sceneSource.getScene();
-            if (mCallback != null) {
-                mCallback.mLoadedScene = mActiveScene;
-                mCallback.run();
-            }
-
-            String shortName = modelName.substring(0, modelName.lastIndexOf('.'));
-            new A3DLoaderTask().execute(shortName + ".a3d");
-        }
-    }
-
-    private class A3DLoaderTask extends AsyncTask<String, Void, Boolean> {
-        protected Boolean doInBackground(String... names) {
-            long start = System.currentTimeMillis();
-            FileA3D model;
-            if (!mLoadFromSD) {
-                model = FileA3D.createFromAsset(mRS, mRes.getAssets(), names[0]);
-            } else {
-                model = FileA3D.createFromFile(mRS, names[0]);
-            }
-            int numModels = model.getIndexEntryCount();
-            for (int i = 0; i < numModels; i ++) {
-                FileA3D.IndexEntry entry = model.getIndexEntry(i);
-                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                    mActiveScene.meshLoaded(entry.getMesh());
-                }
-            }
-            long end = System.currentTimeMillis();
-            Log.v("TIMER", "A3D load time: " + (end - start));
-            return new Boolean(true);
-        }
-
-        protected void onPostExecute(Boolean result) {
-        }
-    }
-
-}
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
deleted file mode 100644
index 9274b17..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import com.android.scenegraph.SceneManager;
-
-import android.renderscript.*;
-import android.renderscript.Float3;
-import android.renderscript.Matrix4f;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class CompoundTransform extends Transform {
-
-    public static abstract class Component {
-        String mName;
-        CompoundTransform mParent;
-        int mParentIndex;
-        protected ScriptField_TransformComponent_s.Item mData;
-
-        Component(int type, String name) {
-            mData = new ScriptField_TransformComponent_s.Item();
-            mData.type = type;
-            mName = name;
-        }
-
-        void setNameAlloc() {
-            RenderScriptGL rs = SceneManager.getRS();
-            if (mData.name != null)  {
-                return;
-            }
-            mData.name = SceneManager.getCachedAlloc(getName());
-            if (mData.name == null) {
-                mData.name = SceneManager.getStringAsAllocation(rs, getName());
-                SceneManager.cacheAlloc(getName(), mData.name);
-            }
-        }
-
-        ScriptField_TransformComponent_s.Item getRSData() {
-            setNameAlloc();
-            return mData;
-        }
-
-        protected void update() {
-            if (mParent != null) {
-                mParent.updateRSComponent(this);
-            }
-        }
-
-        public String getName() {
-            return mName;
-        }
-    }
-
-    public static class TranslateComponent extends Component {
-        public TranslateComponent(String name, Float3 translate) {
-            super(ScriptC_export.const_Transform_TRANSLATE, name);
-            setValue(translate);
-        }
-        public Float3 getValue() {
-            return new Float3(mData.value.x, mData.value.y, mData.value.z);
-        }
-        public void setValue(Float3 val) {
-            mData.value.x = val.x;
-            mData.value.y = val.y;
-            mData.value.z = val.z;
-            update();
-        }
-    }
-
-    public static class RotateComponent extends Component {
-        public RotateComponent(String name, Float3 axis, float angle) {
-            super(ScriptC_export.const_Transform_ROTATE, name);
-            setAxis(axis);
-            setAngle(angle);
-        }
-        public Float3 getAxis() {
-            return new Float3(mData.value.x, mData.value.y, mData.value.z);
-        }
-        public float getAngle() {
-            return mData.value.w;
-        }
-        public void setAxis(Float3 val) {
-            mData.value.x = val.x;
-            mData.value.y = val.y;
-            mData.value.z = val.z;
-            update();
-        }
-        public void setAngle(float val) {
-            mData.value.w = val;
-            update();
-        }
-    }
-
-    public static class ScaleComponent extends Component {
-        public ScaleComponent(String name, Float3 scale) {
-            super(ScriptC_export.const_Transform_SCALE, name);
-            setValue(scale);
-        }
-        public Float3 getValue() {
-            return new Float3(mData.value.x, mData.value.y, mData.value.z);
-        }
-        public void setValue(Float3 val) {
-            mData.value.x = val.x;
-            mData.value.y = val.y;
-            mData.value.z = val.z;
-            update();
-        }
-    }
-
-    ScriptField_TransformComponent_s mComponentField;
-    public ArrayList<Component> mTransformComponents;
-
-    public CompoundTransform() {
-        mTransformComponents = new ArrayList<Component>();
-    }
-
-    public TranslateComponent addTranslate(String name, Float3 translate) {
-        TranslateComponent c = new TranslateComponent(name, translate);
-        addComponent(c);
-        return c;
-    }
-
-    public RotateComponent addRotate(String name, Float3 axis, float angle) {
-        RotateComponent c = new RotateComponent(name, axis, angle);
-        addComponent(c);
-        return c;
-    }
-
-    public ScaleComponent addScale(String name, Float3 scale) {
-        ScaleComponent c = new ScaleComponent(name, scale);
-        addComponent(c);
-        return c;
-    }
-
-    public void addComponent(Component c) {
-        if (c.mParent != null) {
-            throw new IllegalArgumentException("Transform components may not be shared");
-        }
-        c.mParent = this;
-        c.mParentIndex = mTransformComponents.size();
-        mTransformComponents.add(c);
-        updateRSComponentAllocation();
-    }
-
-    public void setComponent(int index, Component c) {
-        if (c.mParent != null) {
-            throw new IllegalArgumentException("Transform components may not be shared");
-        }
-        if (index >= mTransformComponents.size()) {
-            throw new IllegalArgumentException("Invalid component index");
-        }
-        c.mParent = this;
-        c.mParentIndex = index;
-        mTransformComponents.set(index, c);
-        updateRSComponent(c);
-    }
-
-    void updateRSComponent(Component c) {
-        if (mField == null || mComponentField == null) {
-            return;
-        }
-        mComponentField.set(c.getRSData(), c.mParentIndex, true);
-        mField.set_isDirty(0, 1, true);
-    }
-
-    void updateRSComponentAllocation() {
-        if (mField == null) {
-            return;
-        }
-        initLocalData();
-
-        mField.set_components(0, mTransformData.components, false);
-        mField.set_isDirty(0, 1, true);
-    }
-
-    void initLocalData() {
-        RenderScriptGL rs = SceneManager.getRS();
-        int numComponenets = mTransformComponents.size();
-        if (numComponenets > 0) {
-            mComponentField = new ScriptField_TransformComponent_s(rs, numComponenets);
-            for (int i = 0; i < numComponenets; i ++) {
-                Component ith = mTransformComponents.get(i);
-                mComponentField.set(ith.getRSData(), i, false);
-            }
-            mComponentField.copyAll();
-
-            mTransformData.components = mComponentField.getAllocation();
-        }
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java
deleted file mode 100644
index 1502458..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Float4Param.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import com.android.scenegraph.Scene;
-import com.android.scenegraph.SceneManager;
-
-import android.renderscript.Element;
-import android.renderscript.Float4;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class Float4Param extends ShaderParam {
-    private static String TAG = "Float4Param";
-
-    LightBase mLight;
-
-    public Float4Param(String name) {
-        super(name);
-    }
-
-    public Float4Param(String name, float x) {
-        super(name);
-        set(x, 0, 0, 0);
-    }
-
-    public Float4Param(String name, float x, float y) {
-        super(name);
-        set(x, y, 0, 0);
-    }
-
-    public Float4Param(String name, float x, float y, float z) {
-        super(name);
-        set(x, y, z, 0);
-    }
-
-    public Float4Param(String name, float x, float y, float z, float w) {
-        super(name);
-        set(x, y, z, w);
-    }
-
-    void set(float x, float y, float z, float w) {
-        mData.float_value.x = x;
-        mData.float_value.y = y;
-        mData.float_value.z = z;
-        mData.float_value.w = w;
-        if (mField != null) {
-            mField.set_float_value(0, mData.float_value, true);
-        }
-        incTimestamp();
-    }
-
-    public void setValue(Float4 v) {
-        set(v.x, v.y, v.z, v.w);
-    }
-
-    public Float4 getValue() {
-        return mData.float_value;
-    }
-
-    public void setLight(LightBase l) {
-        mLight = l;
-        if (mField != null) {
-            mData.light = mLight.getRSData().getAllocation();
-            mField.set_light(0, mData.light, true);
-        }
-        incTimestamp();
-    }
-
-    boolean findLight(String property) {
-        String indexStr = mParamName.substring(property.length() + 1);
-        if (indexStr == null) {
-            Log.e(TAG, "Invalid light index.");
-            return false;
-        }
-        int index = Integer.parseInt(indexStr);
-        if (index == -1) {
-            return false;
-        }
-        Scene parentScene = SceneManager.getInstance().getActiveScene();
-        ArrayList<LightBase> allLights = parentScene.getLights();
-        if (index >= allLights.size()) {
-            return false;
-        }
-        mLight = allLights.get(index);
-        if (mLight == null) {
-            return false;
-        }
-        return true;
-    }
-
-    int getTypeFromName() {
-        int paramType = ScriptC_export.const_ShaderParam_FLOAT4_DATA;
-        if (mParamName.equalsIgnoreCase(cameraPos)) {
-            paramType = ScriptC_export.const_ShaderParam_FLOAT4_CAMERA_POS;
-        } else if(mParamName.equalsIgnoreCase(cameraDir)) {
-            paramType = ScriptC_export.const_ShaderParam_FLOAT4_CAMERA_DIR;
-        } else if(mParamName.startsWith(lightColor) && findLight(lightColor)) {
-            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_COLOR;
-        } else if(mParamName.startsWith(lightPos) && findLight(lightPos)) {
-            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_POS;
-        } else if(mParamName.startsWith(lightDir) && findLight(lightDir)) {
-            paramType = ScriptC_export.const_ShaderParam_FLOAT4_LIGHT_DIR;
-        }
-        return paramType;
-    }
-
-    void initLocalData() {
-        mData.type = getTypeFromName();
-        if (mCamera != null) {
-            mData.camera = mCamera.getRSData().getAllocation();
-        }
-        if (mLight != null) {
-            mData.light = mLight.getRSData().getAllocation();
-        }
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
deleted file mode 100644
index 8a468db..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import com.android.scenegraph.TextureBase;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.renderscript.ProgramFragment.Builder;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class FragmentShader extends Shader {
-    ProgramFragment mProgram;
-    ScriptField_FragmentShader_s mField;
-
-    public static class Builder {
-
-        FragmentShader mShader;
-        ProgramFragment.Builder mBuilder;
-
-        public Builder(RenderScriptGL rs) {
-            mShader = new FragmentShader();
-            mBuilder = new ProgramFragment.Builder(rs);
-        }
-
-        public Builder setShader(Resources resources, int resourceID) {
-            mBuilder.setShader(resources, resourceID);
-            return this;
-        }
-
-        public Builder setShader(String code) {
-            mBuilder.setShader(code);
-            return this;
-        }
-
-        public Builder setObjectConst(Type type) {
-            mShader.mPerObjConstants = type;
-            return this;
-        }
-
-        public Builder setShaderConst(Type type) {
-            mShader.mPerShaderConstants = type;
-            return this;
-        }
-
-        public Builder addShaderTexture(Program.TextureType texType, String name) {
-            mShader.mShaderTextureNames.add(name);
-            mShader.mShaderTextureTypes.add(texType);
-            return this;
-        }
-
-        public Builder addTexture(Program.TextureType texType, String name) {
-            mShader.mTextureNames.add(name);
-            mShader.mTextureTypes.add(texType);
-            return this;
-        }
-
-        public FragmentShader create() {
-            if (mShader.mPerShaderConstants != null) {
-                mBuilder.addConstant(mShader.mPerShaderConstants);
-            }
-            if (mShader.mPerObjConstants != null) {
-                mBuilder.addConstant(mShader.mPerObjConstants);
-            }
-            for (int i = 0; i < mShader.mTextureTypes.size(); i ++) {
-                mBuilder.addTexture(mShader.mTextureTypes.get(i),
-                                    mShader.mTextureNames.get(i));
-            }
-            for (int i = 0; i < mShader.mShaderTextureTypes.size(); i ++) {
-                mBuilder.addTexture(mShader.mShaderTextureTypes.get(i),
-                                    mShader.mShaderTextureNames.get(i));
-            }
-
-            mShader.mProgram = mBuilder.create();
-            return mShader;
-        }
-    }
-
-    public ProgramFragment getProgram() {
-        return mProgram;
-    }
-
-    ScriptField_ShaderParam_s getTextureParams() {
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (rs == null || res == null) {
-            return null;
-        }
-
-        ArrayList<ScriptField_ShaderParam_s.Item> paramList;
-        paramList = new ArrayList<ScriptField_ShaderParam_s.Item>();
-
-        int shaderTextureStart = mTextureTypes.size();
-        for (int i = 0; i < mShaderTextureNames.size(); i ++) {
-            ShaderParam sp = mSourceParams.get(mShaderTextureNames.get(i));
-            if (sp != null && sp instanceof TextureParam) {
-                TextureParam p = (TextureParam)sp;
-                ScriptField_ShaderParam_s.Item paramRS = new ScriptField_ShaderParam_s.Item();
-                paramRS.bufferOffset = shaderTextureStart + i;
-                paramRS.transformTimestamp = 0;
-                paramRS.dataTimestamp = 0;
-                paramRS.data = p.getRSData().getAllocation();
-                paramList.add(paramRS);
-            }
-        }
-
-        ScriptField_ShaderParam_s rsParams = null;
-        int paramCount = paramList.size();
-        if (paramCount != 0) {
-            rsParams = new ScriptField_ShaderParam_s(rs, paramCount);
-            for (int i = 0; i < paramCount; i++) {
-                rsParams.set(paramList.get(i), i, false);
-            }
-            rsParams.copyAll();
-        }
-        return rsParams;
-    }
-
-    ScriptField_FragmentShader_s getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (rs == null || res == null) {
-            return null;
-        }
-
-        ScriptField_FragmentShader_s.Item item = new ScriptField_FragmentShader_s.Item();
-        item.program = mProgram;
-
-        ScriptField_ShaderParam_s texParams = getTextureParams();
-        if (texParams != null) {
-            item.shaderTextureParams = texParams.getAllocation();
-        }
-
-        linkConstants(rs);
-        if (mPerShaderConstants != null) {
-            item.shaderConst = mConstantBuffer;
-            item.shaderConstParams = mConstantBufferParams.getAllocation();
-            mProgram.bindConstants(item.shaderConst, 0);
-        }
-
-        item.objectConstIndex = -1;
-        if (mPerObjConstants != null) {
-            item.objectConstIndex = mPerShaderConstants != null ? 1 : 0;
-        }
-
-        mField = new ScriptField_FragmentShader_s(rs, 1);
-        mField.set(item, 0, true);
-        return mField;
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java
deleted file mode 100644
index 8f5e2e7..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/LightBase.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.Float3;
-import android.renderscript.Float4;
-import android.renderscript.Matrix4f;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public abstract class LightBase extends SceneGraphBase {
-    static final int RS_LIGHT_POINT = 0;
-    static final int RS_LIGHT_DIRECTIONAL = 1;
-
-    ScriptField_Light_s mField;
-    ScriptField_Light_s.Item mFieldData;
-    Transform mTransform;
-    Float4 mColor;
-    float mIntensity;
-    public LightBase() {
-        mColor = new Float4(0.0f, 0.0f, 0.0f, 0.0f);
-        mIntensity = 1.0f;
-    }
-
-    public void setTransform(Transform t) {
-        mTransform = t;
-        updateRSData();
-    }
-
-    public void setColor(float r, float g, float b) {
-        mColor.x = r;
-        mColor.y = g;
-        mColor.z = b;
-        updateRSData();
-    }
-
-    public void setColor(Float3 c) {
-        setColor(c.x, c.y, c.z);
-    }
-
-    public void setIntensity(float i) {
-        mIntensity = i;
-        updateRSData();
-    }
-
-    public void setName(String n) {
-        super.setName(n);
-        updateRSData();
-    }
-
-    protected void updateRSData() {
-        if (mField == null) {
-            return;
-        }
-        RenderScriptGL rs = SceneManager.getRS();
-        mFieldData.transformMatrix = mTransform.getRSData().getAllocation();
-        mFieldData.name = getNameAlloc(rs);
-        mFieldData.color = mColor;
-        mFieldData.intensity = mIntensity;
-
-        initLocalData();
-
-        mField.set(mFieldData, 0, true);
-    }
-
-    abstract void initLocalData();
-
-    ScriptField_Light_s getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        if (rs == null) {
-            return null;
-        }
-        if (mField == null) {
-            mField = new ScriptField_Light_s(rs, 1);
-            mFieldData = new ScriptField_Light_s.Item();
-        }
-
-        updateRSData();
-
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java
deleted file mode 100644
index 6d70bc9..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/MatrixTransform.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.Matrix4f;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class MatrixTransform extends Transform {
-
-    Matrix4f mLocalMatrix;
-    public MatrixTransform() {
-        mLocalMatrix = new Matrix4f();
-    }
-
-    public void setMatrix(Matrix4f matrix) {
-        mLocalMatrix = matrix;
-        updateRSData();
-    }
-
-    public Matrix4f getMatrix() {
-        return new Matrix4f(mLocalMatrix.getArray());
-    }
-
-    void initLocalData() {
-        mTransformData.localMat = mLocalMatrix;
-    }
-
-    void updateRSData() {
-        if (mField == null) {
-            return;
-        }
-        mField.set_localMat(0, mLocalMatrix, false);
-        mField.set_isDirty(0, 1, true);
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java
deleted file mode 100644
index 574bafc..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/PointLight.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class PointLight extends LightBase {
-    public PointLight() {
-    }
-
-     void initLocalData() {
-        mFieldData.type = RS_LIGHT_POINT;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java
deleted file mode 100644
index 02fd69d..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderPass.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.util.Log;
-
-import android.renderscript.*;
-import android.content.res.Resources;
-
-/**
- * @hide
- */
-public class RenderPass extends SceneGraphBase {
-
-    TextureRenderTarget mColorTarget;
-    Float4 mClearColor;
-    boolean mShouldClearColor;
-
-    TextureRenderTarget mDepthTarget;
-    float mClearDepth;
-    boolean mShouldClearDepth;
-
-    ArrayList<RenderableBase> mObjectsToDraw;
-
-    Camera mCamera;
-
-    ScriptField_RenderPass_s.Item mRsField;
-
-    public RenderPass() {
-        mObjectsToDraw = new ArrayList<RenderableBase>();
-        mClearColor = new Float4(0.0f, 0.0f, 0.0f, 0.0f);
-        mShouldClearColor = true;
-        mClearDepth = 1.0f;
-        mShouldClearDepth = true;
-    }
-
-    public void appendRenderable(Renderable d) {
-        mObjectsToDraw.add(d);
-    }
-
-    public void setCamera(Camera c) {
-        mCamera = c;
-    }
-
-    public void setColorTarget(TextureRenderTarget colorTarget) {
-        mColorTarget = colorTarget;
-    }
-    public void setClearColor(Float4 clearColor) {
-        mClearColor = clearColor;
-    }
-    public void setShouldClearColor(boolean shouldClearColor) {
-        mShouldClearColor = shouldClearColor;
-    }
-
-    public void setDepthTarget(TextureRenderTarget depthTarget) {
-        mDepthTarget = depthTarget;
-    }
-    public void setClearDepth(float clearDepth) {
-        mClearDepth = clearDepth;
-    }
-    public void setShouldClearDepth(boolean shouldClearDepth) {
-        mShouldClearDepth = shouldClearDepth;
-    }
-
-    public ArrayList<RenderableBase> getRenderables() {
-        return mObjectsToDraw;
-    }
-
-    ScriptField_RenderPass_s.Item getRsField(RenderScriptGL rs, Resources res) {
-        if (mRsField != null) {
-            return mRsField;
-        }
-
-        mRsField = new ScriptField_RenderPass_s.Item();
-        if (mColorTarget != null) {
-            mRsField.color_target = mColorTarget.getRsData(true).get_texture(0);
-        }
-        if (mColorTarget != null) {
-            mRsField.depth_target = mDepthTarget.getRsData(true).get_texture(0);
-        }
-        mRsField.camera = mCamera != null ? mCamera.getRSData().getAllocation() : null;
-
-        if (mObjectsToDraw.size() != 0) {
-            Allocation drawableData = Allocation.createSized(rs,
-                                                              Element.ALLOCATION(rs),
-                                                              mObjectsToDraw.size());
-            Allocation[] drawableAllocs = new Allocation[mObjectsToDraw.size()];
-            for (int i = 0; i < mObjectsToDraw.size(); i ++) {
-                Renderable dI = (Renderable)mObjectsToDraw.get(i);
-                drawableAllocs[i] = dI.getRsField(rs, res).getAllocation();
-            }
-            drawableData.copyFrom(drawableAllocs);
-            mRsField.objects = drawableData;
-        }
-
-        mRsField.clear_color = mClearColor;
-        mRsField.clear_depth = mClearDepth;
-        mRsField.should_clear_color = mShouldClearColor;
-        mRsField.should_clear_depth = mShouldClearDepth;
-        return mRsField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java
deleted file mode 100644
index c08a722..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderState.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-import android.content.res.Resources;
-
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramRaster;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.RSRuntimeException;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class RenderState extends SceneGraphBase {
-    VertexShader mVertex;
-    FragmentShader mFragment;
-    ProgramStore mStore;
-    ProgramRaster mRaster;
-
-    ScriptField_RenderState_s mField;
-
-    public RenderState(VertexShader pv,
-                       FragmentShader pf,
-                       ProgramStore ps,
-                       ProgramRaster pr) {
-        mVertex = pv;
-        mFragment = pf;
-        mStore = ps;
-        mRaster = pr;
-    }
-
-    public RenderState(RenderState r) {
-        mVertex = r.mVertex;
-        mFragment = r.mFragment;
-        mStore = r.mStore;
-        mRaster = r.mRaster;
-    }
-
-    public void setProgramVertex(VertexShader pv) {
-        mVertex = pv;
-        updateRSData();
-    }
-
-    public void setProgramFragment(FragmentShader pf) {
-        mFragment = pf;
-        updateRSData();
-    }
-
-    public void setProgramStore(ProgramStore ps) {
-        mStore = ps;
-        updateRSData();
-    }
-
-    public void setProgramRaster(ProgramRaster pr) {
-        mRaster = pr;
-        updateRSData();
-    }
-
-    void updateRSData() {
-        if (mField == null) {
-            return;
-        }
-        ScriptField_RenderState_s.Item item = new ScriptField_RenderState_s.Item();
-        item.pv = mVertex.getRSData().getAllocation();
-        item.pf = mFragment.getRSData().getAllocation();
-        item.ps = mStore;
-        item.pr = mRaster;
-
-        mField.set(item, 0, true);
-    }
-
-    public ScriptField_RenderState_s getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        if (rs == null) {
-            return null;
-        }
-
-        mField = new ScriptField_RenderState_s(rs, 1);
-        updateRSData();
-
-        return mField;
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
deleted file mode 100644
index 9266f30..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-
-import com.android.scenegraph.Float4Param;
-import com.android.scenegraph.MatrixTransform;
-import com.android.scenegraph.SceneManager;
-import com.android.scenegraph.ShaderParam;
-import com.android.scenegraph.TransformParam;
-
-import android.content.res.Resources;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Element.DataType;
-import android.renderscript.Matrix4f;
-import android.renderscript.Mesh;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class Renderable extends RenderableBase {
-    HashMap<String, ShaderParam> mSourceParams;
-
-    RenderState mRenderState;
-    Transform mTransform;
-
-    String mMeshName;
-    String mMeshIndexName;
-
-    public String mMaterialName;
-
-    ScriptField_Renderable_s mField;
-    ScriptField_Renderable_s.Item mData;
-
-    public Renderable() {
-        mSourceParams = new HashMap<String, ShaderParam>();
-        mData = new ScriptField_Renderable_s.Item();
-    }
-
-    public void setCullType(int cull) {
-        mData.cullType = cull;
-    }
-
-    public void setRenderState(RenderState renderState) {
-        mRenderState = renderState;
-        if (mField != null) {
-            RenderScriptGL rs = SceneManager.getRS();
-            updateFieldItem(rs);
-            mField.set(mData, 0, true);
-        }
-    }
-
-    public void setMesh(Mesh mesh) {
-        mData.mesh = mesh;
-        if (mField != null) {
-            mField.set_mesh(0, mData.mesh, true);
-        }
-    }
-
-    public void setMesh(String mesh, String indexName) {
-        mMeshName = mesh;
-        mMeshIndexName = indexName;
-    }
-
-    public void setMaterialName(String name) {
-        mMaterialName = name;
-    }
-
-    public Transform getTransform() {
-        return mTransform;
-    }
-
-    public void setTransform(Transform t) {
-        mTransform = t;
-        if (mField != null) {
-            RenderScriptGL rs = SceneManager.getRS();
-            updateFieldItem(rs);
-            mField.set(mData, 0, true);
-        }
-    }
-
-    public void appendSourceParams(ShaderParam p) {
-        mSourceParams.put(p.getParamName(), p);
-        // Possibly lift this restriction later
-        if (mField != null) {
-            throw new RuntimeException("Can't add source params to objects that are rendering");
-        }
-    }
-
-    public void resolveMeshData(Mesh mesh) {
-        mData.mesh = mesh;
-        if (mData.mesh == null) {
-            Log.v("DRAWABLE: ", "*** NO MESH *** " + mMeshName);
-            return;
-        }
-        int subIndexCount = mData.mesh.getPrimitiveCount();
-        if (subIndexCount == 1 || mMeshIndexName == null) {
-            mData.meshIndex = 0;
-        } else {
-            for (int i = 0; i < subIndexCount; i ++) {
-                if (mData.mesh.getIndexSetAllocation(i).getName().equals(mMeshIndexName)) {
-                    mData.meshIndex = i;
-                    break;
-                }
-            }
-        }
-        if (mField != null) {
-            mField.set(mData, 0, true);
-        }
-    }
-
-    void updateTextures(RenderScriptGL rs) {
-        Iterator<ShaderParam> allParamsIter = mSourceParams.values().iterator();
-        int paramIndex = 0;
-        while (allParamsIter.hasNext()) {
-            ShaderParam sp = allParamsIter.next();
-            if (sp instanceof TextureParam) {
-                TextureParam p = (TextureParam)sp;
-                TextureBase tex = p.getTexture();
-                if (tex != null) {
-                    mData.pf_textures[paramIndex++] = tex.getRsData(false).getAllocation();
-                }
-            }
-        }
-        ProgramFragment pf = mRenderState.mFragment.mProgram;
-        mData.pf_num_textures = pf != null ? Math.min(pf.getTextureCount(), paramIndex) : 0;
-        if (mField != null) {
-            mField.set_pf_textures(0, mData.pf_textures, true);
-            mField.set_pf_num_textures(0, mData.pf_num_textures, true);
-        }
-    }
-
-    public void setVisible(boolean vis) {
-        mData.cullType = vis ? 0 : 2;
-        if (mField != null) {
-            mField.set_cullType(0, mData.cullType, true);
-        }
-    }
-
-    ScriptField_Renderable_s getRsField(RenderScriptGL rs, Resources res) {
-        if (mField != null) {
-            return mField;
-        }
-        updateFieldItem(rs);
-        updateTextures(rs);
-
-        mField = new ScriptField_Renderable_s(rs, 1);
-        mField.set(mData, 0, true);
-
-        return mField;
-    }
-
-    void updateVertexConstants(RenderScriptGL rs) {
-        Allocation pvParams = null, vertexConstants = null;
-        VertexShader pv = mRenderState.mVertex;
-        if (pv != null && pv.getObjectConstants() != null) {
-            vertexConstants = Allocation.createTyped(rs, pv.getObjectConstants());
-            Element vertexConst = vertexConstants.getType().getElement();
-            pvParams = ShaderParam.fillInParams(vertexConst, mSourceParams,
-                                                mTransform).getAllocation();
-        }
-        mData.pv_const = vertexConstants;
-        mData.pv_constParams = pvParams;
-    }
-
-    void updateFragmentConstants(RenderScriptGL rs) {
-        Allocation pfParams = null, fragmentConstants = null;
-        FragmentShader pf = mRenderState.mFragment;
-        if (pf != null && pf.getObjectConstants() != null) {
-            fragmentConstants = Allocation.createTyped(rs, pf.getObjectConstants());
-            Element fragmentConst = fragmentConstants.getType().getElement();
-            pfParams = ShaderParam.fillInParams(fragmentConst, mSourceParams,
-                                                mTransform).getAllocation();
-        }
-        mData.pf_const = fragmentConstants;
-        mData.pf_constParams = pfParams;
-    }
-
-    void updateFieldItem(RenderScriptGL rs) {
-        if (mRenderState == null) {
-            mRenderState = SceneManager.getDefaultState();
-        }
-        if (mTransform == null) {
-            mTransform = SceneManager.getDefaultTransform();
-        }
-        updateVertexConstants(rs);
-        updateFragmentConstants(rs);
-
-        mData.transformMatrix = mTransform.getRSData().getAllocation();
-
-        mData.name = getNameAlloc(rs);
-        mData.render_state = mRenderState.getRSData().getAllocation();
-        mData.bVolInitialized = 0;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
deleted file mode 100644
index 74535dd..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableBase.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class RenderableBase extends SceneGraphBase {
-    public RenderableBase() {
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java
deleted file mode 100644
index 590bbab..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/RenderableGroup.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class RenderableGroup extends RenderableBase {
-
-    ArrayList<RenderableBase> mChildren;
-
-    public RenderableGroup() {
-        mChildren = new ArrayList<RenderableBase>();
-    }
-
-    public void appendChildren(RenderableBase d) {
-        mChildren.add(d);
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
deleted file mode 100644
index 27336ab..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.android.scenegraph.Camera;
-import com.android.scenegraph.CompoundTransform;
-import com.android.scenegraph.RenderPass;
-import com.android.scenegraph.Renderable;
-import com.android.scenegraph.SceneManager;
-import com.android.scenegraph.TextureBase;
-
-import android.content.res.Resources;
-import android.os.AsyncTask;
-import android.renderscript.*;
-import android.renderscript.Mesh;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class Scene extends SceneGraphBase {
-    private static String TIMER_TAG = "TIMER";
-
-    CompoundTransform mRootTransforms;
-    HashMap<String, Transform> mTransformMap;
-    ArrayList<RenderPass> mRenderPasses;
-    ArrayList<LightBase> mLights;
-    ArrayList<Camera> mCameras;
-    ArrayList<FragmentShader> mFragmentShaders;
-    ArrayList<VertexShader> mVertexShaders;
-    ArrayList<RenderableBase> mRenderables;
-    HashMap<String, RenderableBase> mRenderableMap;
-    ArrayList<Texture2D> mTextures;
-
-    HashMap<String, ArrayList<Renderable> > mRenderableMeshMap;
-
-    // RS Specific stuff
-    ScriptField_SgTransform mTransformRSData;
-
-    RenderScriptGL mRS;
-    Resources mRes;
-
-    ScriptField_RenderPass_s mRenderPassAlloc;
-
-    public Scene() {
-        mRenderPasses = new ArrayList<RenderPass>();
-        mLights = new ArrayList<LightBase>();
-        mCameras = new ArrayList<Camera>();
-        mFragmentShaders = new ArrayList<FragmentShader>();
-        mVertexShaders = new ArrayList<VertexShader>();
-        mRenderables = new ArrayList<RenderableBase>();
-        mRenderableMap = new HashMap<String, RenderableBase>();
-        mRenderableMeshMap = new HashMap<String, ArrayList<Renderable> >();
-        mTextures = new ArrayList<Texture2D>();
-        mRootTransforms = new CompoundTransform();
-        mRootTransforms.setName("_scene_root_");
-        mTransformMap = new HashMap<String, Transform>();
-    }
-
-    public void appendTransform(Transform t) {
-        if (t == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mRootTransforms.appendChild(t);
-    }
-
-    public CompoundTransform appendNewCompoundTransform() {
-        CompoundTransform t = new CompoundTransform();
-        appendTransform(t);
-        return t;
-    }
-
-    public MatrixTransform appendNewMatrixTransform() {
-        MatrixTransform t = new MatrixTransform();
-        appendTransform(t);
-        return t;
-    }
-
-    // temporary
-    public void addToTransformMap(Transform t) {
-        mTransformMap.put(t.getName(), t);
-    }
-
-    public Transform getTransformByName(String name) {
-        return mTransformMap.get(name);
-    }
-
-    public void appendRenderPass(RenderPass p) {
-        if (p == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mRenderPasses.add(p);
-    }
-
-    public RenderPass appendNewRenderPass() {
-        RenderPass p = new RenderPass();
-        appendRenderPass(p);
-        return p;
-    }
-
-    public void clearRenderPasses() {
-        mRenderPasses.clear();
-    }
-
-    public void appendLight(LightBase l) {
-        if (l == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mLights.add(l);
-    }
-
-    public void appendCamera(Camera c) {
-        if (c == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mCameras.add(c);
-    }
-
-    public Camera appendNewCamera() {
-        Camera c = new Camera();
-        appendCamera(c);
-        return c;
-    }
-
-    public void appendShader(FragmentShader f) {
-        if (f == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mFragmentShaders.add(f);
-    }
-
-    public void appendShader(VertexShader v) {
-        if (v == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mVertexShaders.add(v);
-    }
-
-    public ArrayList<Camera> getCameras() {
-        return mCameras;
-    }
-
-    public ArrayList<LightBase> getLights() {
-        return mLights;
-    }
-
-    public void appendRenderable(RenderableBase d) {
-        if (d == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mRenderables.add(d);
-        if (d.getName() != null) {
-            mRenderableMap.put(d.getName(), d);
-        }
-    }
-
-    public Renderable appendNewRenderable() {
-        Renderable r = new Renderable();
-        appendRenderable(r);
-        return r;
-    }
-
-    public ArrayList<RenderableBase> getRenderables() {
-        return mRenderables;
-    }
-
-    public RenderableBase getRenderableByName(String name) {
-        return mRenderableMap.get(name);
-    }
-
-    public void appendTextures(Texture2D tex) {
-        if (tex == null) {
-            throw new RuntimeException("Adding null object");
-        }
-        mTextures.add(tex);
-    }
-
-    public void assignRenderStateToMaterial(RenderState renderState, String regex) {
-        Pattern pattern = Pattern.compile(regex);
-        int numRenderables = mRenderables.size();
-        for (int i = 0; i < numRenderables; i ++) {
-            Renderable shape = (Renderable)mRenderables.get(i);
-            Matcher m = pattern.matcher(shape.mMaterialName);
-            if (m.find()) {
-                shape.setRenderState(renderState);
-            }
-        }
-    }
-
-    public void assignRenderState(RenderState renderState) {
-        int numRenderables = mRenderables.size();
-        for (int i = 0; i < numRenderables; i ++) {
-            Renderable shape = (Renderable)mRenderables.get(i);
-            shape.setRenderState(renderState);
-        }
-    }
-
-    public void meshLoaded(Mesh m) {
-        ArrayList<Renderable> entries = mRenderableMeshMap.get(m.getName());
-        int numEntries = entries.size();
-        for (int i = 0; i < numEntries; i++) {
-            Renderable d = entries.get(i);
-            d.resolveMeshData(m);
-        }
-    }
-
-    void addToMeshMap(Renderable d) {
-        ArrayList<Renderable> entries = mRenderableMeshMap.get(d.mMeshName);
-        if (entries == null) {
-            entries = new ArrayList<Renderable>();
-            mRenderableMeshMap.put(d.mMeshName, entries);
-        }
-        entries.add(d);
-    }
-
-    public void destroyRS() {
-        SceneManager sceneManager = SceneManager.getInstance();
-        mTransformRSData = null;
-        sceneManager.mRenderLoop.bind_gRootNode(mTransformRSData);
-        sceneManager.mRenderLoop.set_gRenderableObjects(null);
-        mRenderPassAlloc = null;
-        sceneManager.mRenderLoop.set_gRenderPasses(null);
-        sceneManager.mRenderLoop.bind_gFrontToBack(null);
-        sceneManager.mRenderLoop.bind_gBackToFront(null);
-        sceneManager.mRenderLoop.set_gCameras(null);
-
-        mTransformMap = null;
-        mRenderPasses = null;
-        mLights = null;
-        mCameras = null;
-        mRenderables = null;
-        mRenderableMap = null;
-        mTextures = null;
-        mRenderableMeshMap = null;
-        mRootTransforms = null;
-    }
-
-    public void initRenderPassRS(RenderScriptGL rs, SceneManager sceneManager) {
-        if (mRenderPasses.size() != 0) {
-            mRenderPassAlloc = new ScriptField_RenderPass_s(mRS, mRenderPasses.size());
-            for (int i = 0; i < mRenderPasses.size(); i ++) {
-                mRenderPassAlloc.set(mRenderPasses.get(i).getRsField(mRS, mRes), i, false);
-            }
-            mRenderPassAlloc.copyAll();
-            sceneManager.mRenderLoop.set_gRenderPasses(mRenderPassAlloc.getAllocation());
-        }
-    }
-
-    private void addDrawables(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
-        Allocation drawableData = Allocation.createSized(rs,
-                                                         Element.ALLOCATION(rs),
-                                                         mRenderables.size());
-        Allocation[] drawableAllocs = new Allocation[mRenderables.size()];
-        for (int i = 0; i < mRenderables.size(); i ++) {
-            Renderable dI = (Renderable)mRenderables.get(i);
-            addToMeshMap(dI);
-            drawableAllocs[i] = dI.getRsField(rs, res).getAllocation();
-        }
-        drawableData.copyFrom(drawableAllocs);
-        sceneManager.mRenderLoop.set_gRenderableObjects(drawableData);
-
-        initRenderPassRS(rs, sceneManager);
-    }
-
-    private void addShaders(RenderScriptGL rs, Resources res, SceneManager sceneManager) {
-        if (mVertexShaders.size() > 0) {
-            Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs),
-                                                           mVertexShaders.size());
-            Allocation[] shaderAllocs = new Allocation[mVertexShaders.size()];
-            for (int i = 0; i < mVertexShaders.size(); i ++) {
-                VertexShader sI = mVertexShaders.get(i);
-                shaderAllocs[i] = sI.getRSData().getAllocation();
-            }
-            shaderData.copyFrom(shaderAllocs);
-            sceneManager.mRenderLoop.set_gVertexShaders(shaderData);
-        }
-
-        if (mFragmentShaders.size() > 0) {
-            Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs),
-                                                           mFragmentShaders.size());
-            Allocation[] shaderAllocs = new Allocation[mFragmentShaders.size()];
-            for (int i = 0; i < mFragmentShaders.size(); i ++) {
-                FragmentShader sI = mFragmentShaders.get(i);
-                shaderAllocs[i] = sI.getRSData().getAllocation();
-            }
-            shaderData.copyFrom(shaderAllocs);
-            sceneManager.mRenderLoop.set_gFragmentShaders(shaderData);
-        }
-    }
-
-    public void initRS() {
-        SceneManager sceneManager = SceneManager.getInstance();
-        mRS = SceneManager.getRS();
-        mRes = SceneManager.getRes();
-        long start = System.currentTimeMillis();
-        mTransformRSData = mRootTransforms.getRSData();
-        long end = System.currentTimeMillis();
-        Log.v(TIMER_TAG, "Transform init time: " + (end - start));
-
-        start = System.currentTimeMillis();
-
-        sceneManager.mRenderLoop.bind_gRootNode(mTransformRSData);
-        end = System.currentTimeMillis();
-        Log.v(TIMER_TAG, "Script init time: " + (end - start));
-
-        start = System.currentTimeMillis();
-        addDrawables(mRS, mRes, sceneManager);
-        end = System.currentTimeMillis();
-        Log.v(TIMER_TAG, "Renderable init time: " + (end - start));
-
-        addShaders(mRS, mRes, sceneManager);
-
-        Allocation opaqueBuffer = null;
-        if (mRenderables.size() > 0) {
-            opaqueBuffer = Allocation.createSized(mRS, Element.U32(mRS), mRenderables.size());
-        }
-        Allocation transparentBuffer = null;
-        if (mRenderables.size() > 0) {
-            transparentBuffer = Allocation.createSized(mRS, Element.U32(mRS), mRenderables.size());
-        }
-
-        sceneManager.mRenderLoop.bind_gFrontToBack(opaqueBuffer);
-        sceneManager.mRenderLoop.bind_gBackToFront(transparentBuffer);
-
-        if (mCameras.size() > 0) {
-            Allocation cameraData;
-            cameraData = Allocation.createSized(mRS, Element.ALLOCATION(mRS), mCameras.size());
-            Allocation[] cameraAllocs = new Allocation[mCameras.size()];
-            for (int i = 0; i < mCameras.size(); i ++) {
-                cameraAllocs[i] = mCameras.get(i).getRSData().getAllocation();
-            }
-            cameraData.copyFrom(cameraAllocs);
-            sceneManager.mRenderLoop.set_gCameras(cameraData);
-        }
-
-        if (mLights.size() > 0) {
-            Allocation lightData = Allocation.createSized(mRS,
-                                                          Element.ALLOCATION(mRS),
-                                                          mLights.size());
-            Allocation[] lightAllocs = new Allocation[mLights.size()];
-            for (int i = 0; i < mLights.size(); i ++) {
-                lightAllocs[i] = mLights.get(i).getRSData().getAllocation();
-            }
-            lightData.copyFrom(lightAllocs);
-            sceneManager.mRenderLoop.set_gLights(lightData);
-        }
-    }
-}
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java
deleted file mode 100644
index 412ffbf..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneGraphBase.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import com.android.scenegraph.SceneManager;
-
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.RSRuntimeException;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public abstract class SceneGraphBase {
-    String mName;
-    Allocation mNameAlloc;
-    public void setName(String n) {
-        mName = n;
-        mNameAlloc = null;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    Allocation getNameAlloc(RenderScriptGL rs) {
-        if (mNameAlloc == null)  {
-            mNameAlloc = SceneManager.getStringAsAllocation(rs, getName());
-        }
-        return mNameAlloc;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
deleted file mode 100644
index 4ff2c8b..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.lang.Math;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.android.scenegraph.Camera;
-import com.android.scenegraph.FragmentShader;
-import com.android.scenegraph.MatrixTransform;
-import com.android.scenegraph.Scene;
-import com.android.scenegraph.VertexShader;
-import com.android.testapp.R;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
-import android.renderscript.*;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Mesh;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-import android.view.SurfaceHolder;
-
-/**
- * @hide
- */
-public class SceneManager extends SceneGraphBase {
-
-    HashMap<String, Allocation> mAllocationMap;
-
-    ScriptC_render mRenderLoop;
-    ScriptC mCameraScript;
-    ScriptC mLightScript;
-    ScriptC mObjectParamsScript;
-    ScriptC mFragmentParamsScript;
-    ScriptC mVertexParamsScript;
-    ScriptC mCullScript;
-    ScriptC_transform mTransformScript;
-    ScriptC_export mExportScript;
-
-    RenderScriptGL mRS;
-    Resources mRes;
-    Mesh mQuad;
-    int mWidth;
-    int mHeight;
-
-    Scene mActiveScene;
-    private static SceneManager sSceneManager;
-
-    private Allocation mDefault2D;
-    private Allocation mDefaultCube;
-
-    private FragmentShader mColor;
-    private FragmentShader mTexture;
-    private VertexShader mDefaultVertex;
-
-    private RenderState mDefaultState;
-    private Transform mDefaultTransform;
-
-    private static Allocation getDefault(boolean isCube) {
-        final int dimension = 4;
-        final int bytesPerPixel = 4;
-        int arraySize = dimension * dimension * bytesPerPixel;
-
-        RenderScriptGL rs = sSceneManager.mRS;
-        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));
-        b.setX(dimension).setY(dimension);
-        if (isCube) {
-            b.setFaces(true);
-            arraySize *= 6;
-        }
-        Type bitmapType = b.create();
-
-        Allocation.MipmapControl mip = Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
-        int usage =  Allocation.USAGE_GRAPHICS_TEXTURE;
-        Allocation defaultImage = Allocation.createTyped(rs, bitmapType, mip, usage);
-
-        byte imageData[] = new byte[arraySize];
-        defaultImage.copyFrom(imageData);
-        return defaultImage;
-    }
-
-    static Allocation getDefaultTex2D() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        if (sSceneManager.mDefault2D == null) {
-            sSceneManager.mDefault2D = getDefault(false);
-        }
-        return sSceneManager.mDefault2D;
-    }
-
-    static Allocation getDefaultTexCube() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        if (sSceneManager.mDefaultCube == null) {
-            sSceneManager.mDefaultCube = getDefault(true);
-        }
-        return sSceneManager.mDefaultCube;
-    }
-
-    public static boolean isSDCardPath(String path) {
-        int sdCardIndex = path.indexOf("sdcard/");
-        // We are looking for /sdcard/ or sdcard/
-        if (sdCardIndex == 0 || sdCardIndex == 1) {
-            return true;
-        }
-        sdCardIndex = path.indexOf("mnt/sdcard/");
-        if (sdCardIndex == 0 || sdCardIndex == 1) {
-            return true;
-        }
-        return false;
-    }
-
-    static Bitmap loadBitmap(String name, Resources res) {
-        InputStream is = null;
-        boolean loadFromSD = isSDCardPath(name);
-        try {
-            if (!loadFromSD) {
-                is = res.getAssets().open(name);
-            } else {
-                File f = new File(name);
-                is = new BufferedInputStream(new FileInputStream(f));
-            }
-        } catch (IOException e) {
-            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
-            return null;
-        }
-
-        Bitmap b = BitmapFactory.decodeStream(is);
-        try {
-            is.close();
-        } catch (IOException e) {
-            Log.e("ImageLoaderTask", " Message: " + e.getMessage());
-        }
-        return b;
-    }
-
-    static Allocation createFromBitmap(Bitmap b, RenderScriptGL rs, boolean isCube) {
-        if (b == null) {
-            return null;
-        }
-        MipmapControl mip = MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE;
-        int usage = Allocation.USAGE_GRAPHICS_TEXTURE;
-        if (isCube) {
-            return Allocation.createCubemapFromBitmap(rs, b, mip, usage);
-        }
-        return Allocation.createFromBitmap(rs, b, mip, usage);
-    }
-
-    public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) {
-        return createFromBitmap(loadBitmap(name, res), rs, true);
-    }
-
-    public static Allocation loadCubemap(int id, RenderScriptGL rs, Resources res) {
-        return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, true);
-    }
-
-    public static Allocation loadTexture2D(String name, RenderScriptGL rs, Resources res) {
-        return createFromBitmap(loadBitmap(name, res), rs, false);
-    }
-
-    public static Allocation loadTexture2D(int id, RenderScriptGL rs, Resources res) {
-        return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, false);
-    }
-
-    public static ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) {
-        ProgramStore.Builder builder = new ProgramStore.Builder(rs);
-        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
-        builder.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE);
-        builder.setDitherEnabled(false);
-        builder.setDepthMaskEnabled(false);
-        return builder.create();
-    }
-
-    static Allocation getStringAsAllocation(RenderScript rs, String str) {
-        if (str == null) {
-            return null;
-        }
-        if (str.length() == 0) {
-            return null;
-        }
-        byte[] allocArray = null;
-        byte[] nullChar = new byte[1];
-        nullChar[0] = 0;
-        try {
-            allocArray = str.getBytes("UTF-8");
-            Allocation alloc = Allocation.createSized(rs, Element.U8(rs),
-                                                      allocArray.length + 1,
-                                                      Allocation.USAGE_SCRIPT);
-            alloc.copy1DRangeFrom(0, allocArray.length, allocArray);
-            alloc.copy1DRangeFrom(allocArray.length, 1, nullChar);
-            return alloc;
-        }
-        catch (Exception e) {
-            throw new RSRuntimeException("Could not convert string to utf-8.");
-        }
-    }
-
-    static Allocation getCachedAlloc(String str) {
-        if (sSceneManager == null) {
-            throw new RuntimeException("Scene manager not initialized");
-        }
-        return sSceneManager.mAllocationMap.get(str);
-    }
-
-    static void cacheAlloc(String str, Allocation alloc) {
-        if (sSceneManager == null) {
-            throw new RuntimeException("Scene manager not initialized");
-        }
-        sSceneManager.mAllocationMap.put(str, alloc);
-    }
-
-    public static class SceneLoadedCallback implements Runnable {
-        public Scene mLoadedScene;
-        public String mName;
-        public void run() {
-        }
-    }
-
-    public Scene getActiveScene() {
-        return mActiveScene;
-    }
-
-    public void setActiveScene(Scene s) {
-        mActiveScene = s;
-
-        if (mActiveScene == null) {
-            return;
-        }
-
-        // Do some sanity checking
-        if (mActiveScene.getCameras().size() == 0) {
-            Matrix4f camPos = new Matrix4f();
-            camPos.translate(0, 0, 10);
-            MatrixTransform cameraTransform = new MatrixTransform();
-            cameraTransform.setName("_DefaultCameraTransform");
-            cameraTransform.setMatrix(camPos);
-            mActiveScene.appendTransform(cameraTransform);
-            Camera cam = new Camera();
-            cam.setName("_DefaultCamera");
-            cam.setTransform(cameraTransform);
-            mActiveScene.appendCamera(cam);
-        }
-
-        mActiveScene.appendShader(getDefaultVS());
-        mActiveScene.appendTransform(getDefaultTransform());
-    }
-
-    static RenderScriptGL getRS() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        return sSceneManager.mRS;
-    }
-
-    static Resources getRes() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        return sSceneManager.mRes;
-    }
-
-    // Provides the folowing inputs to fragment shader
-    // Assigned by default if nothing is present
-    // vec3 varWorldPos;
-    // vec3 varWorldNormal;
-    // vec2 varTex0;
-    public static VertexShader getDefaultVS() {
-        if (sSceneManager == null) {
-            return null;
-        }
-
-        if (sSceneManager.mDefaultVertex == null) {
-            RenderScriptGL rs = getRS();
-            Element.Builder b = new Element.Builder(rs);
-            b.add(Element.MATRIX_4X4(rs), "model");
-            Type.Builder objConstBuilder = new Type.Builder(rs, b.create());
-
-            b = new Element.Builder(rs);
-            b.add(Element.MATRIX_4X4(rs), "viewProj");
-            Type.Builder shaderConstBuilder = new Type.Builder(rs, b.create());
-
-            b = new Element.Builder(rs);
-            b.add(Element.F32_4(rs), "position");
-            b.add(Element.F32_2(rs), "texture0");
-            b.add(Element.F32_3(rs), "normal");
-            Element defaultIn = b.create();
-
-            final String code = "\n" +
-                "varying vec3 varWorldPos;\n" +
-                "varying vec3 varWorldNormal;\n" +
-                "varying vec2 varTex0;\n" +
-                "void main() {" +
-                "   vec4 objPos = ATTRIB_position;\n" +
-                "   vec4 worldPos = UNI_model * objPos;\n" +
-                "   gl_Position = UNI_viewProj * worldPos;\n" +
-                "   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);\n" +
-                "   vec3 worldNorm = model3 * ATTRIB_normal;\n" +
-                "   varWorldPos = worldPos.xyz;\n" +
-                "   varWorldNormal = worldNorm;\n" +
-                "   varTex0 = ATTRIB_texture0;\n" +
-                "}\n";
-
-            VertexShader.Builder sb = new VertexShader.Builder(rs);
-            sb.addInput(defaultIn);
-            sb.setObjectConst(objConstBuilder.setX(1).create());
-            sb.setShaderConst(shaderConstBuilder.setX(1).create());
-            sb.setShader(code);
-            sSceneManager.mDefaultVertex = sb.create();
-        }
-
-        return sSceneManager.mDefaultVertex;
-    }
-
-    public static FragmentShader getColorFS() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        if (sSceneManager.mColor == null) {
-            RenderScriptGL rs = getRS();
-            Element.Builder b = new Element.Builder(rs);
-            b.add(Element.F32_4(rs), "color");
-            Type.Builder objConstBuilder = new Type.Builder(rs, b.create());
-
-            final String code = "\n" +
-                "varying vec2 varTex0;\n" +
-                "void main() {\n" +
-                "   lowp vec4 col = UNI_color;\n" +
-                "   gl_FragColor = col;\n" +
-                "}\n";
-            FragmentShader.Builder fb = new FragmentShader.Builder(rs);
-            fb.setShader(code);
-            fb.setObjectConst(objConstBuilder.create());
-            sSceneManager.mColor = fb.create();
-        }
-
-        return sSceneManager.mColor;
-    }
-
-    public static FragmentShader getTextureFS() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        if (sSceneManager.mTexture == null) {
-            RenderScriptGL rs = getRS();
-
-            final String code = "\n" +
-                "varying vec2 varTex0;\n" +
-                "void main() {\n" +
-                "   lowp vec4 col = texture2D(UNI_color, varTex0).rgba;\n" +
-                "   gl_FragColor = col;\n" +
-                "}\n";
-
-            FragmentShader.Builder fb = new FragmentShader.Builder(rs);
-            fb.setShader(code);
-            fb.addTexture(Program.TextureType.TEXTURE_2D, "color");
-            sSceneManager.mTexture = fb.create();
-            sSceneManager.mTexture.mProgram.bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(rs), 0);
-        }
-
-        return sSceneManager.mTexture;
-    }
-
-    static RenderState getDefaultState() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        if (sSceneManager.mDefaultState == null) {
-            sSceneManager.mDefaultState = new RenderState(getDefaultVS(), getColorFS(), null, null);
-            sSceneManager.mDefaultState.setName("__DefaultState");
-        }
-        return sSceneManager.mDefaultState;
-    }
-
-    static Transform getDefaultTransform() {
-        if (sSceneManager == null) {
-            return null;
-        }
-        if (sSceneManager.mDefaultTransform == null) {
-            sSceneManager.mDefaultTransform = new MatrixTransform();
-            sSceneManager.mDefaultTransform.setName("__DefaultTransform");
-        }
-        return sSceneManager.mDefaultTransform;
-    }
-
-    public static SceneManager getInstance() {
-        if (sSceneManager == null) {
-            sSceneManager = new SceneManager();
-        }
-        return sSceneManager;
-    }
-
-    protected SceneManager() {
-    }
-
-    public void loadModel(String name, SceneLoadedCallback cb) {
-        ColladaScene scene = new ColladaScene(name, cb);
-        scene.init(mRS, mRes);
-    }
-
-    public Mesh getScreenAlignedQuad() {
-        if (mQuad != null) {
-            return mQuad;
-        }
-
-        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
-                                           3, Mesh.TriangleMeshBuilder.TEXTURE_0);
-
-        tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 1.0f);
-        tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 1.0f);
-        tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 1.0f);
-        tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 1.0f);
-
-        tmb.addTriangle(0, 1, 2);
-        tmb.addTriangle(2, 3, 0);
-
-        mQuad = tmb.create(true);
-        return mQuad;
-    }
-
-    public Renderable getRenderableQuad(String name, RenderState state) {
-        Renderable quad = new Renderable();
-        quad.setTransform(new MatrixTransform());
-        quad.setMesh(getScreenAlignedQuad());
-        quad.setName(name);
-        quad.setRenderState(state);
-        quad.setCullType(1);
-        return quad;
-    }
-
-    public void initRS(RenderScriptGL rs, Resources res, int w, int h) {
-        mRS = rs;
-        mRes = res;
-        mAllocationMap = new HashMap<String, Allocation>();
-
-        mQuad = null;
-        mDefault2D = null;
-        mDefaultCube = null;
-        mDefaultVertex = null;
-        mColor = null;
-        mTexture = null;
-        mDefaultState = null;
-        mDefaultTransform = null;
-
-        mExportScript = new ScriptC_export(rs, res, R.raw.export);
-
-        mTransformScript = new ScriptC_transform(rs, res, R.raw.transform);
-        mTransformScript.set_gTransformScript(mTransformScript);
-
-        mCameraScript = new ScriptC_camera(rs, res, R.raw.camera);
-        mLightScript = new ScriptC_light(rs, res, R.raw.light);
-        mObjectParamsScript = new ScriptC_object_params(rs, res, R.raw.object_params);
-        mFragmentParamsScript = new ScriptC_object_params(rs, res, R.raw.fragment_params);
-        mVertexParamsScript = new ScriptC_object_params(rs, res, R.raw.vertex_params);
-        mCullScript = new ScriptC_cull(rs, res, R.raw.cull);
-
-        mRenderLoop = new ScriptC_render(rs, res, R.raw.render);
-        mRenderLoop.set_gTransformScript(mTransformScript);
-        mRenderLoop.set_gCameraScript(mCameraScript);
-        mRenderLoop.set_gLightScript(mLightScript);
-        mRenderLoop.set_gObjectParamsScript(mObjectParamsScript);
-        mRenderLoop.set_gFragmentParamsScript(mFragmentParamsScript);
-        mRenderLoop.set_gVertexParamsScript(mVertexParamsScript);
-        mRenderLoop.set_gCullScript(mCullScript);
-
-        mRenderLoop.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
-    }
-
-    public ScriptC getRenderLoop() {
-        return mRenderLoop;
-    }
-}
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java
deleted file mode 100644
index 4975114..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Shader.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import com.android.scenegraph.SceneGraphBase;
-import com.android.scenegraph.ShaderParam;
-
-import android.renderscript.*;
-import android.renderscript.ProgramFragment.Builder;
-import android.util.Log;
-
-/**
- * @hide
- */
-public abstract class Shader extends SceneGraphBase {
-    protected Type mPerObjConstants;
-    protected Type mPerShaderConstants;
-
-    protected HashMap<String, ShaderParam> mSourceParams;
-    protected ArrayList<String> mShaderTextureNames;
-    protected ArrayList<Program.TextureType > mShaderTextureTypes;
-    protected ArrayList<String> mTextureNames;
-    protected ArrayList<Program.TextureType > mTextureTypes;
-
-    protected Allocation mConstantBuffer;
-    protected ScriptField_ShaderParam_s mConstantBufferParams;
-
-    public Shader() {
-        mSourceParams = new HashMap<String, ShaderParam>();
-        mShaderTextureNames = new ArrayList<String>();
-        mShaderTextureTypes = new ArrayList<Program.TextureType>();
-        mTextureNames = new ArrayList<String>();
-        mTextureTypes = new ArrayList<Program.TextureType>();
-    }
-
-    public void appendSourceParams(ShaderParam p) {
-        mSourceParams.put(p.getParamName(), p);
-    }
-
-    public Type getObjectConstants() {
-        return mPerObjConstants;
-    }
-
-    public Type getShaderConstants() {
-        return mPerObjConstants;
-    }
-
-    void linkConstants(RenderScriptGL rs) {
-        if (mPerShaderConstants == null) {
-            return;
-        }
-
-        Element constElem = mPerShaderConstants.getElement();
-        mConstantBufferParams  = ShaderParam.fillInParams(constElem, mSourceParams, null);
-
-        mConstantBuffer = Allocation.createTyped(rs, mPerShaderConstants);
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
deleted file mode 100644
index 3dd41ca..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-import com.android.scenegraph.SceneManager;
-import com.android.scenegraph.Transform;
-
-import android.renderscript.Element;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.RenderScriptGL;
-import android.util.Log;
-
-/**
- * @hide
- */
-public abstract class ShaderParam extends SceneGraphBase {
-
-    static final String cameraPos        = "cameraPos";
-    static final String cameraDir        = "cameraDir";
-
-    static final String lightColor       = "lightColor";
-    static final String lightPos         = "lightPos";
-    static final String lightDir         = "lightDir";
-
-    static final String view             = "view";
-    static final String proj             = "proj";
-    static final String viewProj         = "viewProj";
-    static final String model            = "model";
-    static final String modelView        = "modelView";
-    static final String modelViewProj    = "modelViewProj";
-
-    static final long sMaxTimeStamp = 0xffffffffL;
-
-    ScriptField_ShaderParamData_s.Item mData;
-    ScriptField_ShaderParamData_s mField;
-
-    String mParamName;
-    Camera mCamera;
-
-    static ScriptField_ShaderParam_s fillInParams(Element constantElem,
-                                                  HashMap<String, ShaderParam> sourceParams,
-                                                  Transform transform) {
-        RenderScriptGL rs = SceneManager.getRS();
-        ArrayList<ScriptField_ShaderParam_s.Item> paramList;
-        paramList = new ArrayList<ScriptField_ShaderParam_s.Item>();
-
-        int subElemCount = constantElem.getSubElementCount();
-        for (int i = 0; i < subElemCount; i ++) {
-            String inputName = constantElem.getSubElementName(i);
-            int offset = constantElem.getSubElementOffsetBytes(i);
-
-            ShaderParam matchingParam = sourceParams.get(inputName);
-            Element subElem = constantElem.getSubElement(i);
-            // Make one if it's not there
-            if (matchingParam == null) {
-                if (subElem.getDataType() == Element.DataType.FLOAT_32) {
-                    matchingParam = new Float4Param(inputName, 0.5f, 0.5f, 0.5f, 0.5f);
-                } else if (subElem.getDataType() == Element.DataType.MATRIX_4X4) {
-                    TransformParam trParam = new TransformParam(inputName);
-                    trParam.setTransform(transform);
-                    matchingParam = trParam;
-                }
-            }
-            ScriptField_ShaderParam_s.Item paramRS = new ScriptField_ShaderParam_s.Item();
-            paramRS.bufferOffset = offset;
-            paramRS.transformTimestamp = 0;
-            paramRS.dataTimestamp = 0;
-            paramRS.data = matchingParam.getRSData().getAllocation();
-            if (subElem.getDataType() == Element.DataType.FLOAT_32) {
-                paramRS.float_vecSize = subElem.getVectorSize();
-            }
-
-            paramList.add(paramRS);
-        }
-
-        ScriptField_ShaderParam_s rsParams = null;
-        int paramCount = paramList.size();
-        if (paramCount != 0) {
-            rsParams = new ScriptField_ShaderParam_s(rs, paramCount);
-            for (int i = 0; i < paramCount; i++) {
-                rsParams.set(paramList.get(i), i, false);
-            }
-            rsParams.copyAll();
-        }
-        return rsParams;
-    }
-
-    public ShaderParam(String name) {
-        mParamName = name;
-        mData = new ScriptField_ShaderParamData_s.Item();
-    }
-
-    public String getParamName() {
-        return mParamName;
-    }
-
-    public void setCamera(Camera c) {
-        mCamera = c;
-        if (mField != null) {
-            mData.camera = mCamera.getRSData().getAllocation();
-            mField.set_camera(0, mData.camera, true);
-        }
-    }
-
-    protected void incTimestamp() {
-        if (mField != null) {
-            mData.timestamp ++;
-            mData.timestamp %= sMaxTimeStamp;
-            mField.set_timestamp(0, mData.timestamp, true);
-        }
-    }
-
-    abstract void initLocalData();
-
-    public ScriptField_ShaderParamData_s getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        mField = new ScriptField_ShaderParamData_s(rs, 1);
-
-        if (mParamName != null) {
-            mData.paramName = SceneManager.getCachedAlloc(mParamName);
-            if (mData.paramName == null) {
-                mData.paramName = SceneManager.getStringAsAllocation(rs, mParamName);
-                SceneManager.cacheAlloc(mParamName, mData.paramName);
-            }
-        }
-        initLocalData();
-        mData.timestamp = 1;
-
-        mField.set(mData, 0, true);
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
deleted file mode 100644
index b53ab88..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-
-import com.android.scenegraph.SceneManager;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class Texture2D extends TextureBase {
-    String mFileName;
-    String mFileDir;
-    int mResourceID;
-
-    public Texture2D() {
-        super(ScriptC_export.const_TextureType_TEXTURE_2D);
-    }
-
-    public Texture2D(Allocation tex) {
-        super(ScriptC_export.const_TextureType_TEXTURE_2D);
-        setTexture(tex);
-    }
-
-    public Texture2D(String dir, String file) {
-        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
-        setFileDir(dir);
-        setFileName(file);
-    }
-
-    public Texture2D(int resourceID) {
-        super(ScriptC_export.const_TextureType_TEXTURE_2D);
-        mResourceID = resourceID;
-    }
-
-    public void setFileDir(String dir) {
-        mFileDir = dir;
-    }
-
-    public void setFileName(String file) {
-        mFileName = file;
-    }
-
-    public String getFileName() {
-        return mFileName;
-    }
-
-    public void setTexture(Allocation tex) {
-        mData.texture = tex != null ? tex : SceneManager.getDefaultTex2D();
-        if (mField != null) {
-            mField.set_texture(0, mData.texture, true);
-        }
-    }
-
-    void load() {
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (mFileName != null && mFileName.length() > 0) {
-            String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
-            setTexture(SceneManager.loadTexture2D(mFileDir + shortName, rs, res));
-        } else if (mResourceID != 0) {
-            setTexture(SceneManager.loadTexture2D(mResourceID, rs, res));
-        }
-    }
-
-    ScriptField_Texture_s getRsData(boolean loadNow) {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (rs == null || res == null) {
-            return null;
-        }
-
-        mField = new ScriptField_Texture_s(rs, 1);
-
-        if (loadNow) {
-            load();
-        } else {
-            mData.texture = SceneManager.getDefaultTex2D();
-            new SingleImageLoaderTask().execute(this);
-        }
-
-        mField.set(mData, 0, true);
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java
deleted file mode 100644
index ba49d4e..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureBase.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-
-import com.android.scenegraph.SceneManager;
-import android.os.AsyncTask;
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-/**
- * @hide
- */
-public abstract class TextureBase extends SceneGraphBase {
-
-    class SingleImageLoaderTask extends AsyncTask<TextureBase, Void, Boolean> {
-        protected Boolean doInBackground(TextureBase... objects) {
-            TextureBase tex = objects[0];
-            tex.load();
-            return new Boolean(true);
-        }
-        protected void onPostExecute(Boolean result) {
-        }
-    }
-
-    ScriptField_Texture_s.Item mData;
-    ScriptField_Texture_s mField;
-    TextureBase(int type) {
-        mData = new ScriptField_Texture_s.Item();
-        mData.type = type;
-    }
-
-    protected Allocation mRsTexture;
-    abstract ScriptField_Texture_s getRsData(boolean loadNow);
-    abstract void load();
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
deleted file mode 100644
index 1269e3c..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-
-import com.android.scenegraph.SceneManager;
-import com.android.scenegraph.TextureBase;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class TextureCube extends TextureBase {
-    String mFileName;
-    String mFileDir;
-    int mResourceID;
-
-    public TextureCube() {
-        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
-    }
-
-    public TextureCube(Allocation tex) {
-        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
-        setTexture(tex);
-    }
-
-    public TextureCube(String dir, String file) {
-        super(ScriptC_export.const_TextureType_TEXTURE_CUBE);
-        setFileDir(dir);
-        setFileName(file);
-    }
-
-    public TextureCube(int resourceID) {
-        super(ScriptC_export.const_TextureType_TEXTURE_2D);
-        mResourceID = resourceID;
-    }
-
-    public void setFileDir(String dir) {
-        mFileDir = dir;
-    }
-
-    public void setFileName(String file) {
-        mFileName = file;
-    }
-
-    public String getFileName() {
-        return mFileName;
-    }
-
-    public void setTexture(Allocation tex) {
-        mData.texture = tex != null ? tex : SceneManager.getDefaultTexCube();
-        if (mField != null) {
-            mField.set_texture(0, mData.texture, true);
-        }
-    }
-
-    void load() {
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (mFileName != null && mFileName.length() > 0) {
-            String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1);
-            setTexture(SceneManager.loadCubemap(mFileDir + shortName, rs, res));
-        } else if (mResourceID != 0) {
-            setTexture(SceneManager.loadCubemap(mResourceID , rs, res));
-        }
-    }
-
-    ScriptField_Texture_s getRsData(boolean loadNow) {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (rs == null || res == null) {
-            return null;
-        }
-
-        mField = new ScriptField_Texture_s(rs, 1);
-
-        if (loadNow) {
-            load();
-        } else {
-            mData.texture = SceneManager.getDefaultTexCube();
-            new SingleImageLoaderTask().execute(this);
-        }
-
-        mField.set(mData, 0, true);
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java
deleted file mode 100644
index e656ed2..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureParam.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.graphics.Camera;
-import android.renderscript.RenderScriptGL;
-import android.renderscript.Float4;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.Element;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class TextureParam extends ShaderParam {
-
-    TextureBase mTexture;
-
-    public TextureParam(String name) {
-        super(name);
-    }
-
-    public TextureParam(String name, TextureBase t) {
-        super(name);
-        setTexture(t);
-    }
-
-    public void setTexture(TextureBase t) {
-        mTexture = t;
-    }
-
-    public TextureBase getTexture() {
-        return mTexture;
-    }
-
-    void initLocalData() {
-        mData.type = ScriptC_export.const_ShaderParam_TEXTURE;
-        if (mTexture != null) {
-            mData.texture = mTexture.getRsData(false).getAllocation();
-        }
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java
deleted file mode 100644
index 6aa29a5..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureRenderTarget.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-
-import com.android.scenegraph.SceneManager;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class TextureRenderTarget extends TextureBase {
-    public TextureRenderTarget() {
-        super(ScriptC_export.const_TextureType_TEXTURE_RENDER_TARGET);
-    }
-
-    public TextureRenderTarget(Allocation tex) {
-        super(ScriptC_export.const_TextureType_TEXTURE_RENDER_TARGET);
-        setTexture(tex);
-    }
-
-    public void setTexture(Allocation tex) {
-        mData.texture = tex;
-        if (mField != null) {
-            mField.set_texture(0, mData.texture, true);
-        }
-    }
-
-    void load() {
-    }
-
-    ScriptField_Texture_s getRsData(boolean loadNow) {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        if (rs == null) {
-            return null;
-        }
-
-        mField = new ScriptField_Texture_s(rs, 1);
-        mField.set(mData, 0, true);
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java
deleted file mode 100644
index 8180bd0..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Transform.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.*;
-import android.renderscript.Matrix4f;
-import android.util.Log;
-
-/**
- * @hide
- */
-public abstract class Transform extends SceneGraphBase {
-    Transform mParent;
-    ArrayList<Transform> mChildren;
-
-    ScriptField_SgTransform mField;
-    ScriptField_SgTransform.Item mTransformData;
-
-    public Transform() {
-        mChildren = new ArrayList<Transform>();
-        mParent = null;
-    }
-
-    public void appendChild(Transform t) {
-        mChildren.add(t);
-        t.mParent = this;
-        updateRSChildData(true);
-    }
-
-    abstract void initLocalData();
-
-    void updateRSChildData(boolean copyData) {
-        if (mField == null) {
-            return;
-        }
-        RenderScriptGL rs = SceneManager.getRS();
-        if (mChildren.size() != 0) {
-            Allocation childRSData = Allocation.createSized(rs, Element.ALLOCATION(rs),
-                                                            mChildren.size());
-            mTransformData.children = childRSData;
-
-            Allocation[] childrenAllocs = new Allocation[mChildren.size()];
-            for (int i = 0; i < mChildren.size(); i ++) {
-                Transform child = mChildren.get(i);
-                childrenAllocs[i] = child.getRSData().getAllocation();
-            }
-            childRSData.copyFrom(childrenAllocs);
-        }
-        if (copyData) {
-            mField.set(mTransformData, 0, true);
-        }
-    }
-
-    ScriptField_SgTransform getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        if (rs == null) {
-            return null;
-        }
-        mField = new ScriptField_SgTransform(rs, 1);
-
-        mTransformData = new ScriptField_SgTransform.Item();
-        mTransformData.name = getNameAlloc(rs);
-        mTransformData.isDirty = 1;
-        mTransformData.timestamp = 1;
-
-        initLocalData();
-        updateRSChildData(false);
-
-        mField.set(mTransformData, 0, true);
-        return mField;
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java
deleted file mode 100644
index d120d5d..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TransformParam.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.renderscript.RenderScriptGL;
-import android.renderscript.Matrix4f;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.Element;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class TransformParam extends ShaderParam {
-
-    Transform mTransform;
-    LightBase mLight;
-
-    public TransformParam(String name) {
-        super(name);
-    }
-
-    public void setTransform(Transform t) {
-        mTransform = t;
-        if (mField != null && mTransform != null) {
-            mData.transform = mTransform.getRSData().getAllocation();
-        }
-        incTimestamp();
-    }
-
-    int getTypeFromName() {
-        int paramType = ScriptC_export.const_ShaderParam_TRANSFORM_DATA;
-        if (mParamName.equalsIgnoreCase(view)) {
-            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_VIEW;
-        } else if(mParamName.equalsIgnoreCase(proj)) {
-            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_PROJ;
-        } else if(mParamName.equalsIgnoreCase(viewProj)) {
-            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_VIEW_PROJ;
-        } else if(mParamName.equalsIgnoreCase(model)) {
-            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL;
-        } else if(mParamName.equalsIgnoreCase(modelView)) {
-            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL_VIEW;
-        } else if(mParamName.equalsIgnoreCase(modelViewProj)) {
-            paramType = ScriptC_export.const_ShaderParam_TRANSFORM_MODEL_VIEW_PROJ;
-        }
-        return paramType;
-    }
-
-    void initLocalData() {
-        mData.type = getTypeFromName();
-        if (mTransform != null) {
-            mData.transform = mTransform.getRSData().getAllocation();
-        }
-        if (mCamera != null) {
-            mData.camera = mCamera.getRSData().getAllocation();
-        }
-        if (mLight != null) {
-            mData.light = mLight.getRSData().getAllocation();
-        }
-    }
-}
-
-
-
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
deleted file mode 100644
index 4efaff7..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.scenegraph;
-
-import java.lang.Math;
-import java.util.ArrayList;
-
-import android.content.res.Resources;
-import android.renderscript.*;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class VertexShader extends Shader {
-    ProgramVertex mProgram;
-    ScriptField_VertexShader_s mField;
-
-    public static class Builder {
-        VertexShader mShader;
-        ProgramVertex.Builder mBuilder;
-
-        public Builder(RenderScriptGL rs) {
-            mShader = new VertexShader();
-            mBuilder = new ProgramVertex.Builder(rs);
-        }
-
-        public Builder setShader(Resources resources, int resourceID) {
-            mBuilder.setShader(resources, resourceID);
-            return this;
-        }
-
-        public Builder setShader(String code) {
-            mBuilder.setShader(code);
-            return this;
-        }
-
-        public Builder setObjectConst(Type type) {
-            mShader.mPerObjConstants = type;
-            return this;
-        }
-
-        public Builder setShaderConst(Type type) {
-            mShader.mPerShaderConstants = type;
-            return this;
-        }
-
-        public Builder addInput(Element e) {
-            mBuilder.addInput(e);
-            return this;
-        }
-
-        public VertexShader create() {
-            if (mShader.mPerShaderConstants != null) {
-                mBuilder.addConstant(mShader.mPerShaderConstants);
-            }
-            if (mShader.mPerObjConstants != null) {
-                mBuilder.addConstant(mShader.mPerObjConstants);
-            }
-            mShader.mProgram = mBuilder.create();
-            return mShader;
-        }
-    }
-
-    public ProgramVertex getProgram() {
-        return mProgram;
-    }
-
-    ScriptField_VertexShader_s getRSData() {
-        if (mField != null) {
-            return mField;
-        }
-
-        RenderScriptGL rs = SceneManager.getRS();
-        Resources res = SceneManager.getRes();
-        if (rs == null || res == null) {
-            return null;
-        }
-
-        ScriptField_VertexShader_s.Item item = new ScriptField_VertexShader_s.Item();
-        item.program = mProgram;
-
-        linkConstants(rs);
-        if (mPerShaderConstants != null) {
-            item.shaderConst = mConstantBuffer;
-            item.shaderConstParams = mConstantBufferParams.getAllocation();
-            mProgram.bindConstants(item.shaderConst, 0);
-        }
-
-        item.objectConstIndex = -1;
-        if (mPerObjConstants != null) {
-            item.objectConstIndex = mPerShaderConstants != null ? 1 : 0;
-        }
-
-        mField = new ScriptField_VertexShader_s(rs, 1);
-        mField.set(item, 0, true);
-        return mField;
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs
deleted file mode 100644
index dc0a885..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/camera.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-//#define DEBUG_CAMERA
-#include "scenegraph_objects.rsh"
-
-void root(const rs_allocation *v_in, rs_allocation *v_out, const float *usrData) {
-
-    SgCamera *cam = (SgCamera *)rsGetElementAt(*v_in, 0);
-    float aspect = *usrData;
-    if (cam->aspect != aspect) {
-        cam->isDirty = 1;
-        cam->aspect = aspect;
-    }
-    if (cam->isDirty) {
-        rsMatrixLoadPerspective(&cam->proj, cam->horizontalFOV, cam->aspect, cam->near, cam->far);
-    }
-
-    const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
-    //rsDebug("Camera stamp", cam->transformTimestamp);
-    //rsDebug("Transform stamp", camTransform->timestamp);
-    if (camTransform->timestamp != cam->transformTimestamp || cam->isDirty) {
-        cam->isDirty = 1;
-        rs_matrix4x4 camPosMatrix;
-        rsMatrixLoad(&camPosMatrix, &camTransform->globalMat);
-        float4 zero = {0.0f, 0.0f, 0.0f, 1.0f};
-        cam->position = rsMatrixMultiply(&camPosMatrix, zero);
-
-        rsMatrixInverse(&camPosMatrix);
-        rsMatrixLoad(&cam->view, &camPosMatrix);
-
-        rsMatrixLoad(&cam->viewProj, &cam->proj);
-        rsMatrixMultiply(&cam->viewProj, &cam->view);
-
-        rsExtractFrustumPlanes(&cam->viewProj,
-                               &cam->frustumPlanes[0], &cam->frustumPlanes[1],
-                               &cam->frustumPlanes[2], &cam->frustumPlanes[3],
-                               &cam->frustumPlanes[3], &cam->frustumPlanes[4]);
-    }
-
-    if (cam->isDirty) {
-        cam->timestamp ++;
-    }
-
-    cam->isDirty = 0;
-    cam->transformTimestamp = camTransform->timestamp;
-
-#ifdef DEBUG_CAMERA
-    printCameraInfo(cam);
-#endif //DEBUG_CAMERA
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs
deleted file mode 100644
index 024e026..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/cull.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#include "scenegraph_objects.rsh"
-
-static void getTransformedSphere(SgRenderable *obj) {
-    obj->worldBoundingSphere = obj->boundingSphere;
-    obj->worldBoundingSphere.w = 1.0f;
-    const SgTransform *objTransform = (const SgTransform *)rsGetElementAt(obj->transformMatrix, 0);
-    obj->worldBoundingSphere = rsMatrixMultiply(&objTransform->globalMat, obj->worldBoundingSphere);
-
-    const float4 unitVec = {0.57735f, 0.57735f, 0.57735f, 0.0f};
-    float4 scaledVec = rsMatrixMultiply(&objTransform->globalMat, unitVec);
-    scaledVec.w = 0.0f;
-    obj->worldBoundingSphere.w = obj->boundingSphere.w * length(scaledVec);
-}
-
-static bool frustumCulled(SgRenderable *obj, SgCamera *cam) {
-    if (!obj->bVolInitialized) {
-        float minX, minY, minZ, maxX, maxY, maxZ;
-        rsgMeshComputeBoundingBox(obj->mesh,
-                                  &minX, &minY, &minZ,
-                                  &maxX, &maxY, &maxZ);
-        //rsDebug("min", minX, minY, minZ);
-        //rsDebug("max", maxX, maxY, maxZ);
-        float4 sphere;
-        sphere.x = (maxX + minX) * 0.5f;
-        sphere.y = (maxY + minY) * 0.5f;
-        sphere.z = (maxZ + minZ) * 0.5f;
-        float3 radius;
-        radius.x = (maxX - sphere.x);
-        radius.y = (maxY - sphere.y);
-        radius.z = (maxZ - sphere.z);
-
-        sphere.w = length(radius);
-        obj->boundingSphere = sphere;
-        obj->bVolInitialized = 1;
-        //rsDebug("Sphere", sphere);
-    }
-
-    getTransformedSphere(obj);
-
-    return !rsIsSphereInFrustum(&obj->worldBoundingSphere,
-                                &cam->frustumPlanes[0], &cam->frustumPlanes[1],
-                                &cam->frustumPlanes[2], &cam->frustumPlanes[3],
-                                &cam->frustumPlanes[4], &cam->frustumPlanes[5]);
-}
-
-
-void root(rs_allocation *v_out, const void *usrData) {
-
-    SgRenderable *drawable = (SgRenderable *)rsGetElementAt(*v_out, 0);
-    const SgCamera *camera = (const SgCamera*)usrData;
-
-    drawable->isVisible = 0;
-    // Not loaded yet
-    if (!rsIsObject(drawable->mesh) || drawable->cullType == CULL_ALWAYS) {
-        return;
-    }
-
-    // check to see if we are culling this object and if it's
-    // outside the frustum
-    if (drawable->cullType == CULL_FRUSTUM && frustumCulled(drawable, (SgCamera*)camera)) {
-#ifdef DEBUG_RENDERABLES
-        rsDebug("Culled", drawable);
-        printName(drawable->name);
-#endif // DEBUG_RENDERABLES
-        return;
-    }
-    drawable->isVisible = 1;
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs
deleted file mode 100644
index b438a43..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/export.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-// The sole purpose of this script is to have various structs exposed
-// so that java reflected classes are generated
-#include "scenegraph_objects.rsh"
-
-// Export our native constants to java so that we don't have parallel definitions
-const int ShaderParam_FLOAT4_DATA = SHADER_PARAM_FLOAT4_DATA;
-const int ShaderParam_TRANSFORM_DATA = SHADER_PARAM_TRANSFORM_DATA;
-const int ShaderParam_TRANSFORM_MODEL = SHADER_PARAM_TRANSFORM_MODEL;
-
-const int ShaderParam_FLOAT4_CAMERA_POS = SHADER_PARAM_FLOAT4_CAMERA_POS;
-const int ShaderParam_FLOAT4_CAMERA_DIR = SHADER_PARAM_FLOAT4_CAMERA_DIR;
-const int ShaderParam_TRANSFORM_VIEW = SHADER_PARAM_TRANSFORM_VIEW;
-const int ShaderParam_TRANSFORM_PROJ = SHADER_PARAM_TRANSFORM_PROJ;
-const int ShaderParam_TRANSFORM_VIEW_PROJ = SHADER_PARAM_TRANSFORM_VIEW_PROJ;
-const int ShaderParam_TRANSFORM_MODEL_VIEW = SHADER_PARAM_TRANSFORM_MODEL_VIEW;
-const int ShaderParam_TRANSFORM_MODEL_VIEW_PROJ = SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ;
-
-const int ShaderParam_FLOAT4_LIGHT_COLOR = SHADER_PARAM_FLOAT4_LIGHT_COLOR;
-const int ShaderParam_FLOAT4_LIGHT_POS = SHADER_PARAM_FLOAT4_LIGHT_POS;
-const int ShaderParam_FLOAT4_LIGHT_DIR = SHADER_PARAM_FLOAT4_LIGHT_DIR;
-
-const int ShaderParam_TEXTURE = SHADER_PARAM_TEXTURE;
-
-const int Transform_TRANSLATE = TRANSFORM_TRANSLATE;
-const int Transform_ROTATE = TRANSFORM_ROTATE;
-const int Transform_SCALE = TRANSFORM_SCALE;
-
-const int TextureType_TEXTURE_2D = TEXTURE_2D;
-const int TextureType_TEXTURE_CUBE = TEXTURE_CUBE;
-const int TextureType_TEXTURE_RENDER_TARGET = TEXTURE_RENDER_TARGET;
-
-SgTransform *exportPtr;
-SgTransformComponent *componentPtr;
-SgRenderState *sExport;
-SgRenderable *drExport;
-SgRenderPass *pExport;
-SgCamera *exportPtrCam;
-SgLight *exportPtrLight;
-SgShaderParam *spExport;
-SgShaderParamData *spDataExport;
-SgVertexShader *pvExport;
-SgFragmentShader *pfExport;
-SgTexture *texExport;
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs
deleted file mode 100644
index 7202285..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/fragment_params.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#include "scenegraph_objects.rsh"
-
-//#define DEBUG_PARAMS
-
-#include "params.rsh"
-
-void root(rs_allocation *v_out, const void *usrData) {
-    SgFragmentShader *shader = (SgFragmentShader *)rsGetElementAt(*v_out, 0);
-    const SgCamera *camera = (const SgCamera*)usrData;
-    processAllParams(shader->shaderConst, shader->shaderConstParams, camera);
-    processTextureParams(shader);
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs
deleted file mode 100644
index e11979f..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/light.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-//#define DEBUG_LIGHT
-#include "scenegraph_objects.rsh"
-
-void root(const rs_allocation *v_in, rs_allocation *v_out) {
-
-    SgLight *light = (SgLight *)rsGetElementAt(*v_in, 0);
-    const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
-
-    float4 zero = {0.0f, 0.0f, 0.0f, 1.0f};
-    light->position = rsMatrixMultiply(&lTransform->globalMat, zero);
-
-#ifdef DEBUG_LIGHT
-    printLightInfo(light);
-#endif //DEBUG_LIGHT
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs
deleted file mode 100644
index 0d524a6..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/object_params.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#include "scenegraph_objects.rsh"
-
-//#define DEBUG_PARAMS
-
-#include "params.rsh"
-
-void root(rs_allocation *v_out, const void *usrData) {
-
-    SgRenderable *drawable = (SgRenderable *)rsGetElementAt(*v_out, 0);
-    // Visibility flag was set earlier in the cull stage
-    if (!drawable->isVisible) {
-        return;
-    }
-
-    const SgCamera *camera = (const SgCamera*)usrData;
-    processAllParams(drawable->pf_const, drawable->pf_constParams, camera);
-    processAllParams(drawable->pv_const, drawable->pv_constParams, camera);
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
deleted file mode 100644
index 00793c0..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/params.rsh
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#include "scenegraph_objects.rsh"
-
-//#define DEBUG_PARAMS
-static inline void debugParam(SgShaderParam *p, SgShaderParamData *pData) {
-    rsDebug("____________ Param ____________", p);
-    printName(pData->paramName);
-    rsDebug("bufferOffset", p->bufferOffset);
-    rsDebug("type ", pData->type);
-    rsDebug("data timestamp ", pData->timestamp);
-    rsDebug("param timestamp", p->dataTimestamp);
-
-    const SgTransform *pTransform = NULL;
-    if (rsIsObject(pData->transform)) {
-        pTransform = (const SgTransform *)rsGetElementAt(pData->transform, 0);
-
-        rsDebug("transform", pTransform);
-        printName(pTransform->name);
-        rsDebug("timestamp", pTransform->timestamp);
-        rsDebug("param timestamp", p->transformTimestamp);
-    }
-
-    const SgLight *pLight = NULL;
-    if (rsIsObject(pData->light)) {
-        pLight = (const SgLight *)rsGetElementAt(pData->light, 0);
-        printLightInfo(pLight);
-    }
-}
-
-static inline void writeFloatData(float *ptr, const float4 *input, uint32_t vecSize) {
-#ifdef DEBUG_PARAMS
-    rsDebug("Writing value ", *input);
-    rsDebug("Writing vec size ", vecSize);
-#endif // DEBUG_PARAMS
-
-    switch (vecSize) {
-    case 1:
-        *ptr = input->x;
-        break;
-    case 2:
-        *((float2*)ptr) = (*input).xy;
-        break;
-    case 3:
-        *((float3*)ptr) = (*input).xyz;
-        break;
-    case 4:
-        *((float4*)ptr) = *input;
-        break;
-    }
-}
-
-static inline bool processParam(SgShaderParam *p, SgShaderParamData *pData,
-                         uint8_t *constantBuffer,
-                         const SgCamera *currentCam,
-                         SgFragmentShader *shader) {
-    bool isDataOnly = (pData->type > SHADER_PARAM_DATA_ONLY);
-    const SgTransform *pTransform = NULL;
-    if (rsIsObject(pData->transform)) {
-        pTransform = (const SgTransform *)rsGetElementAt(pData->transform, 0);
-    }
-
-    if (isDataOnly) {
-        // If we are a transform param and our transform is unchanged, nothing to do
-        if (pTransform) {
-            if (p->transformTimestamp == pTransform->timestamp) {
-                return false;
-            }
-            p->transformTimestamp = pTransform->timestamp;
-        } else {
-            if (p->dataTimestamp == pData->timestamp) {
-                return false;
-            }
-            p->dataTimestamp = pData->timestamp;
-        }
-    }
-
-    const SgLight *pLight = NULL;
-    if (rsIsObject(pData->light)) {
-        pLight = (const SgLight *)rsGetElementAt(pData->light, 0);
-    }
-
-    uint8_t *dataPtr = NULL;
-    const SgTexture *tex = NULL;
-    if (pData->type == SHADER_PARAM_TEXTURE) {
-        tex = rsGetElementAt(pData->texture, 0);
-    } else {
-        dataPtr = constantBuffer + p->bufferOffset;
-    }
-
-    switch (pData->type) {
-    case SHADER_PARAM_TEXTURE:
-        rsgBindTexture(shader->program, p->bufferOffset, tex->texture);
-        break;
-    case SHADER_PARAM_FLOAT4_DATA:
-        writeFloatData((float*)dataPtr, &pData->float_value, p->float_vecSize);
-        break;
-    case SHADER_PARAM_FLOAT4_CAMERA_POS:
-        writeFloatData((float*)dataPtr, &currentCam->position, p->float_vecSize);
-        break;
-    case SHADER_PARAM_FLOAT4_CAMERA_DIR: break;
-    case SHADER_PARAM_FLOAT4_LIGHT_COLOR:
-        writeFloatData((float*)dataPtr, &pLight->color, p->float_vecSize);
-        break;
-    case SHADER_PARAM_FLOAT4_LIGHT_POS:
-        writeFloatData((float*)dataPtr, &pLight->position, p->float_vecSize);
-        break;
-    case SHADER_PARAM_FLOAT4_LIGHT_DIR: break;
-
-    case SHADER_PARAM_TRANSFORM_DATA:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &pTransform->globalMat);
-        break;
-    case SHADER_PARAM_TRANSFORM_VIEW:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->view);
-        break;
-    case SHADER_PARAM_TRANSFORM_PROJ:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->proj);
-        break;
-    case SHADER_PARAM_TRANSFORM_VIEW_PROJ:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->viewProj);
-        break;
-    case SHADER_PARAM_TRANSFORM_MODEL:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &pTransform->globalMat);
-        break;
-    case SHADER_PARAM_TRANSFORM_MODEL_VIEW:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->view);
-        rsMatrixLoadMultiply((rs_matrix4x4*)dataPtr,
-                             (rs_matrix4x4*)dataPtr,
-                             &pTransform->globalMat);
-        break;
-    case SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ:
-        rsMatrixLoad((rs_matrix4x4*)dataPtr, &currentCam->viewProj);
-        rsMatrixLoadMultiply((rs_matrix4x4*)dataPtr,
-                             (rs_matrix4x4*)dataPtr,
-                             &pTransform->globalMat);
-        break;
-    }
-    return true;
-}
-
-static inline void processAllParams(rs_allocation shaderConst,
-                             rs_allocation allParams,
-                             const SgCamera *camera) {
-    if (rsIsObject(shaderConst)) {
-        uint8_t *constantBuffer = (uint8_t*)rsGetElementAt(shaderConst, 0);
-
-        int numParams = 0;
-        if (rsIsObject(allParams)) {
-            numParams = rsAllocationGetDimX(allParams);
-        }
-        bool updated = false;
-        for (int i = 0; i < numParams; i ++) {
-            SgShaderParam *current = (SgShaderParam*)rsGetElementAt(allParams, i);
-            SgShaderParamData *currentData = (SgShaderParamData*)rsGetElementAt(current->data, 0);
-#ifdef DEBUG_PARAMS
-            debugParam(current, currentData);
-#endif // DEBUG_PARAMS
-            updated = processParam(current, currentData, constantBuffer, camera, NULL) || updated;
-        }
-    }
-}
-
-static inline void processTextureParams(SgFragmentShader *shader) {
-    int numParams = 0;
-    if (rsIsObject(shader->shaderTextureParams)) {
-        numParams = rsAllocationGetDimX(shader->shaderTextureParams);
-    }
-    for (int i = 0; i < numParams; i ++) {
-        SgShaderParam *current = (SgShaderParam*)rsGetElementAt(shader->shaderTextureParams, i);
-        SgShaderParamData *currentData = (SgShaderParamData*)rsGetElementAt(current->data, 0);
-#ifdef DEBUG_PARAMS
-        debugParam(current, currentData);
-#endif // DEBUG_PARAMS
-        processParam(current, currentData, NULL, NULL, shader);
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
deleted file mode 100644
index 205b2cb..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright (C) 2011-2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#include "rs_graphics.rsh"
-#include "scenegraph_objects.rsh"
-
-rs_script gTransformScript;
-rs_script gCameraScript;
-rs_script gLightScript;
-rs_script gObjectParamsScript;
-rs_script gFragmentParamsScript;
-rs_script gVertexParamsScript;
-rs_script gCullScript;
-
-SgTransform *gRootNode;
-rs_allocation gCameras;
-rs_allocation gLights;
-rs_allocation gFragmentShaders;
-rs_allocation gVertexShaders;
-rs_allocation gRenderableObjects;
-
-rs_allocation gRenderPasses;
-
-// Temporary shaders
-rs_program_store gPFSBackground;
-
-uint32_t *gFrontToBack;
-static uint32_t gFrontToBackCount = 0;
-uint32_t *gBackToFront;
-static uint32_t gBackToFrontCount = 0;
-
-static SgCamera *gActiveCamera = NULL;
-
-static rs_allocation nullAlloc;
-
-// #define DEBUG_RENDERABLES
-static void draw(SgRenderable *obj) {
-#ifdef DEBUG_RENDERABLES
-    const SgTransform *objTransform = (const SgTransform *)rsGetElementAt(obj->transformMatrix, 0);
-    rsDebug("**** Drawing object with transform", obj);
-    printName(objTransform->name);
-    rsDebug("Model matrix: ", &objTransform->globalMat);
-    printName(obj->name);
-#endif //DEBUG_RENDERABLES
-
-    const SgRenderState *renderState = (const SgRenderState *)rsGetElementAt(obj->render_state, 0);
-    const SgVertexShader *pv = (const SgVertexShader *)rsGetElementAt(renderState->pv, 0);
-    const SgFragmentShader *pf = (const SgFragmentShader *)rsGetElementAt(renderState->pf, 0);
-
-    if (pv->objectConstIndex != -1) {
-        rsgBindConstant(pv->program, pv->objectConstIndex, obj->pv_const);
-    }
-    if (pf->objectConstIndex != -1) {
-        rsgBindConstant(pf->program, pf->objectConstIndex, obj->pf_const);
-    }
-
-    if (rsIsObject(renderState->ps)) {
-        rsgBindProgramStore(renderState->ps);
-    } else {
-        rsgBindProgramStore(gPFSBackground);
-    }
-
-    if (rsIsObject(renderState->pr)) {
-        rsgBindProgramRaster(renderState->pr);
-    } else {
-        rs_program_raster pr = {0};
-        rsgBindProgramRaster(pr);
-    }
-
-    rsgBindProgramVertex(pv->program);
-    rsgBindProgramFragment(pf->program);
-
-    for (uint32_t i = 0; i < obj->pf_num_textures; i ++) {
-        const SgTexture *tex = rsGetElementAt(obj->pf_textures[i], 0);
-        rsgBindTexture(pf->program, i, tex->texture);
-    }
-
-    rsgDrawMesh(obj->mesh, obj->meshIndex);
-}
-
-static void sortToBucket(SgRenderable *obj) {
-    const SgRenderState *renderState = (const SgRenderState *)rsGetElementAt(obj->render_state, 0);
-    if (rsIsObject(renderState->ps)) {
-        bool isOpaque = false;
-        if (isOpaque) {
-            gFrontToBack[gFrontToBackCount++] = (uint32_t)obj;
-        } else {
-            gBackToFront[gBackToFrontCount++] = (uint32_t)obj;
-        }
-    } else {
-        gFrontToBack[gFrontToBackCount++] = (uint32_t)obj;
-    }
-}
-
-static void updateActiveCamera(rs_allocation cam) {
-    gActiveCamera = (SgCamera *)rsGetElementAt(cam, 0);
-}
-
-static void prepareCameras() {
-    // now compute all the camera matrices
-    if (rsIsObject(gCameras)) {
-        float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-        rsForEach(gCameraScript, gCameras, nullAlloc, &aspect, sizeof(aspect));
-    }
-}
-
-static void prepareLights() {
-    if (rsIsObject(gLights)) {
-        rsForEach(gLightScript, gLights, nullAlloc);
-    }
-}
-
-static void drawSorted() {
-    for (int i = 0; i < gFrontToBackCount; i ++) {
-        SgRenderable *current = (SgRenderable*)gFrontToBack[i];
-        draw(current);
-    }
-
-    for (int i = 0; i < gBackToFrontCount; i ++) {
-        SgRenderable *current = (SgRenderable*)gBackToFront[i];
-        draw(current);
-    }
-}
-
-static void drawAllObjects(rs_allocation allObj) {
-    if (!rsIsObject(allObj)) {
-        return;
-    }
-
-    if (rsIsObject(gVertexShaders)) {
-        rsForEach(gVertexParamsScript, nullAlloc, gVertexShaders,
-                  gActiveCamera, sizeof(gActiveCamera));
-    }
-    if (rsIsObject(gFragmentShaders)) {
-        rsForEach(gFragmentParamsScript, nullAlloc, gFragmentShaders,
-                  gActiveCamera, sizeof(gActiveCamera));
-    }
-
-    // Run the params and cull script
-    rsForEach(gCullScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera));
-    rsForEach(gObjectParamsScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera));
-
-    int numRenderables = rsAllocationGetDimX(allObj);
-    for (int i = 0; i < numRenderables; i ++) {
-        rs_allocation *drawAlloc = (rs_allocation*)rsGetElementAt(allObj, i);
-        SgRenderable *current = (SgRenderable*)rsGetElementAt(*drawAlloc, 0);
-        if (current->isVisible) {
-            sortToBucket(current);
-        }
-    }
-    drawSorted();
-}
-
-int root(void) {
-#ifdef DEBUG_RENDERABLES
-    rsDebug("=============================================================================", 0);
-#endif // DEBUG_RENDERABLES
-
-    // first step is to update the transform hierachy
-    if (gRootNode && rsIsObject(gRootNode->children)) {
-        rsForEach(gTransformScript, gRootNode->children, nullAlloc, 0, 0);
-    }
-
-    prepareCameras();
-    prepareLights();
-
-    if (rsIsObject(gRenderPasses)) {
-        rsgClearDepth(1.0f);
-        int numPasses = rsAllocationGetDimX(gRenderPasses);
-        for (uint i = 0; i < numPasses; i ++) {
-            gFrontToBackCount = 0;
-            gBackToFrontCount = 0;
-            SgRenderPass *pass = (SgRenderPass*)rsGetElementAt(gRenderPasses, i);
-            if (rsIsObject(pass->color_target)) {
-                rsgBindColorTarget(pass->color_target, 0);
-            }
-            if (rsIsObject(pass->depth_target)) {
-                rsgBindDepthTarget(pass->depth_target);
-            }
-            if (!rsIsObject(pass->color_target) &&
-                !rsIsObject(pass->depth_target)) {
-                rsgClearAllRenderTargets();
-            }
-            updateActiveCamera(pass->camera);
-            if (pass->should_clear_color) {
-                rsgClearColor(pass->clear_color.x, pass->clear_color.y,
-                              pass->clear_color.z, pass->clear_color.w);
-            }
-            if (pass->should_clear_depth) {
-                rsgClearDepth(pass->clear_depth);
-            }
-            drawAllObjects(pass->objects);
-        }
-    } else {
-        gFrontToBackCount = 0;
-        gBackToFrontCount = 0;
-        rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-        rsgClearDepth(1.0f);
-
-        if (rsIsObject(gCameras)) {
-            rs_allocation *camAlloc = (rs_allocation*)rsGetElementAt(gCameras, 0);
-            updateActiveCamera(*camAlloc);
-        }
-        drawAllObjects(gRenderableObjects);
-    }
-    return 10;
-}
-
-// Search through sorted and culled objects
-void pick(int screenX, int screenY) {
-    float3 pnt, vec;
-    getCameraRay(gActiveCamera, screenX, screenY, &pnt, &vec);
-
-    for (int i = 0; i < gFrontToBackCount; i ++) {
-        SgRenderable *current = (SgRenderable*)gFrontToBack[i];
-        bool isPicked = intersect(current, pnt, vec);
-        if (isPicked) {
-            current->cullType = CULL_ALWAYS;
-        }
-    }
-
-    for (int i = 0; i < gBackToFrontCount; i ++) {
-        SgRenderable *current = (SgRenderable*)gBackToFront[i];
-        bool isPicked = intersect(current, pnt, vec);
-        if (isPicked) {
-            current->cullType = CULL_ALWAYS;
-        }
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
deleted file mode 100644
index 90ae212..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/scenegraph_objects.rsh
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright (C) 2011-2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#ifndef _TRANSFORM_DEF_
-#define _TRANSFORM_DEF_
-
-#include "rs_graphics.rsh"
-
-#define TRANSFORM_NONE      0
-#define TRANSFORM_TRANSLATE 1
-#define TRANSFORM_ROTATE    2
-#define TRANSFORM_SCALE     3
-
-#define CULL_FRUSTUM 0
-#define CULL_ALWAYS  2
-
-#define LIGHT_POINT       0
-#define LIGHT_DIRECTIONAL 1
-
-// Shader params that involve only data
-#define SHADER_PARAM_DATA_ONLY                 10000
-#define SHADER_PARAM_FLOAT4_DATA               10001
-#define SHADER_PARAM_TRANSFORM_DATA            10002
-#define SHADER_PARAM_TRANSFORM_MODEL           10003
-
-// Shader params that involve camera
-#define SHADER_PARAM_CAMERA                    1000
-#define SHADER_PARAM_FLOAT4_CAMERA_POS         1001
-#define SHADER_PARAM_FLOAT4_CAMERA_DIR         1002
-#define SHADER_PARAM_TRANSFORM_VIEW            1003
-#define SHADER_PARAM_TRANSFORM_PROJ            1004
-#define SHADER_PARAM_TRANSFORM_VIEW_PROJ       1005
-#define SHADER_PARAM_TRANSFORM_MODEL_VIEW      1006
-#define SHADER_PARAM_TRANSFORM_MODEL_VIEW_PROJ 1007
-
-// Shader Params that only involve lights
-#define SHADER_PARAM_LIGHT                     100
-#define SHADER_PARAM_FLOAT4_LIGHT_COLOR        103
-#define SHADER_PARAM_FLOAT4_LIGHT_POS          104
-#define SHADER_PARAM_FLOAT4_LIGHT_DIR          105
-
-#define SHADER_PARAM_TEXTURE                   10
-
-#define TEXTURE_NONE          0
-#define TEXTURE_2D            1
-#define TEXTURE_CUBE          2
-#define TEXTURE_RENDER_TARGET 3
-
-typedef struct TransformComponent_s {
-    float4 value;
-    int type;
-    rs_allocation name;
-} SgTransformComponent;
-
-typedef struct __attribute__((packed, aligned(4))) SgTransform {
-    rs_matrix4x4 globalMat;
-    rs_matrix4x4 localMat;
-
-    rs_allocation components;
-    int isDirty;
-
-    rs_allocation children;
-    rs_allocation name;
-
-    // Used to check whether transform params need to be updated
-    uint32_t timestamp;
-} SgTransform;
-
-typedef struct VertexShader_s {
-    rs_program_vertex program;
-    // Buffer with vertex constant data
-    rs_allocation shaderConst;
-    // ShaderParam's that populate data
-    rs_allocation shaderConstParams;
-    // location of the per object constants on the buffer
-    int objectConstIndex;
-} SgVertexShader;
-
-typedef struct FragmentShader_s {
-    rs_program_fragment program;
-    // Buffer with vertex constant data
-    rs_allocation shaderConst;
-    // ShaderParam's that populate data
-    rs_allocation shaderConstParams;
-    // ShaderParam's that set textures
-    rs_allocation shaderTextureParams;
-    // location of the per object constants on the buffer
-    int objectConstIndex;
-} SgFragmentShader;
-
-typedef struct RenderState_s {
-    rs_allocation pv; // VertexShader struct
-    rs_allocation pf; // FragmentShader struct
-    rs_program_store ps;
-    rs_program_raster pr;
-} SgRenderState;
-
-typedef struct Renderable_s {
-    rs_allocation render_state;
-    // Buffer with vertex constant data
-    rs_allocation pv_const;
-    // ShaderParam's that populate data
-    rs_allocation pv_constParams;
-    // Buffer with fragment constant data
-    rs_allocation pf_const;
-    // ShaderParam's that populate data
-    rs_allocation pf_constParams;
-    rs_allocation pf_textures[8];
-    int pf_num_textures;
-    rs_mesh mesh;
-    int meshIndex;
-    rs_allocation transformMatrix;
-    rs_allocation name;
-    float4 boundingSphere;
-    float4 worldBoundingSphere;
-    int bVolInitialized;
-    int cullType; // specifies whether to frustum cull
-    int isVisible;
-} SgRenderable;
-
-typedef struct RenderPass_s {
-    rs_allocation color_target;
-    rs_allocation depth_target;
-    rs_allocation camera;
-    rs_allocation objects;
-
-    float4 clear_color;
-    float clear_depth;
-    bool should_clear_color;
-    bool should_clear_depth;
-} SgRenderPass;
-
-typedef struct Camera_s {
-    rs_matrix4x4 proj;
-    rs_matrix4x4 view;
-    rs_matrix4x4 viewProj;
-    float4 position;
-    float near;
-    float far;
-    float horizontalFOV;
-    float aspect;
-    rs_allocation name;
-    rs_allocation transformMatrix;
-    float4 frustumPlanes[6];
-
-    int isDirty;
-    // Timestamp of the camera itself to signal params if anything changes
-    uint32_t timestamp;
-    // Timestamp of our transform
-    uint32_t transformTimestamp;
-} SgCamera;
-
-typedef struct Light_s {
-    float4 position;
-    float4 color;
-    float intensity;
-    int type;
-    rs_allocation name;
-    rs_allocation transformMatrix;
-} SgLight;
-
-// This represents the shader parameter data needed to set a float or transform data
-typedef struct ShaderParamData_s {
-    int type;
-    float4 float_value;
-    uint32_t timestamp;
-    rs_allocation paramName;
-    rs_allocation camera;
-    rs_allocation light;
-    rs_allocation transform;
-    rs_allocation texture;
-} SgShaderParamData;
-
-// This represents a shader parameter that knows how to update itself for a given
-// renderable or shader and contains a timestamp for the last time this buffer was updated
-typedef struct ShaderParam_s {
-    // Used to check whether transform params need to be updated
-    uint32_t transformTimestamp;
-    // Used to check whether data params need to be updated
-    // These are used when somebody set the matrix of float value directly in java
-    uint32_t dataTimestamp;
-    // Specifies where in the constant buffer data gets written to
-    int bufferOffset;
-    // An instance of SgShaderParamData that could be shared by multiple objects
-    rs_allocation data;
-    // How many components of the vector we need to write
-    int float_vecSize;
-} SgShaderParam;
-
-// This represents a texture object
-typedef struct Texture_s {
-    uint32_t type;
-    rs_allocation texture;
-} SgTexture;
-
-static inline void printName(rs_allocation name) {
-    if (!rsIsObject(name)) {
-        rsDebug("no name", 0);
-        return;
-    }
-
-    rsDebug((const char*)rsGetElementAt(name, 0), 0);
-}
-
-static inline void printCameraInfo(const SgCamera *cam) {
-    rsDebug("***** Camera information. ptr:", cam);
-    printName(cam->name);
-    const SgTransform *camTransform = (const SgTransform *)rsGetElementAt(cam->transformMatrix, 0);
-    rsDebug("Transform name:", camTransform);
-    printName(camTransform->name);
-
-    rsDebug("Aspect: ", cam->aspect);
-    rsDebug("Near: ", cam->near);
-    rsDebug("Far: ", cam->far);
-    rsDebug("Fov: ", cam->horizontalFOV);
-    rsDebug("Position: ", cam->position);
-    rsDebug("Proj: ", &cam->proj);
-    rsDebug("View: ", &cam->view);
-}
-
-static inline void printLightInfo(const SgLight *light) {
-    rsDebug("***** Light information. ptr:", light);
-    printName(light->name);
-    const SgTransform *lTransform = (const SgTransform *)rsGetElementAt(light->transformMatrix, 0);
-    rsDebug("Transform name:", lTransform);
-    printName(lTransform->name);
-
-    rsDebug("Position: ", light->position);
-    rsDebug("Color : ", light->color);
-    rsDebug("Intensity: ", light->intensity);
-    rsDebug("Type: ", light->type);
-}
-
-static inline void getCameraRay(const SgCamera *cam, int screenX, int screenY, float3 *pnt, float3 *vec) {
-    rsDebug("=================================", screenX);
-    rsDebug("Point X", screenX);
-    rsDebug("Point Y", screenY);
-
-    rs_matrix4x4 mvpInv;
-    rsMatrixLoad(&mvpInv, &cam->viewProj);
-    rsMatrixInverse(&mvpInv);
-
-    float width = (float)rsgGetWidth();
-    float height = (float)rsgGetHeight();
-
-    float4 pos = {(float)screenX, height - (float)screenY, 0.0f, 1.0f};
-
-    pos.x /= width;
-    pos.y /= height;
-
-    rsDebug("Pre Norm X", pos.x);
-    rsDebug("Pre Norm Y", pos.y);
-
-    pos.xy = pos.xy * 2.0f - 1.0f;
-
-    rsDebug("Norm X", pos.x);
-    rsDebug("Norm Y", pos.y);
-
-    pos = rsMatrixMultiply(&mvpInv, pos);
-    float oneOverW = 1.0f / pos.w;
-    pos.xyz *= oneOverW;
-
-    rsDebug("World X", pos.x);
-    rsDebug("World Y", pos.y);
-    rsDebug("World Z", pos.z);
-
-    rsDebug("Cam X", cam->position.x);
-    rsDebug("Cam Y", cam->position.y);
-    rsDebug("Cam Z", cam->position.z);
-
-    *vec = normalize(pos.xyz - cam->position.xyz);
-    rsDebug("Vec X", vec->x);
-    rsDebug("Vec Y", vec->y);
-    rsDebug("Vec Z", vec->z);
-    *pnt = cam->position.xyz;
-}
-
-static inline bool intersect(const SgRenderable *obj, float3 pnt, float3 vec) {
-    // Solving for t^2 + Bt + C = 0
-    float3 originMinusCenter = pnt - obj->worldBoundingSphere.xyz;
-    float B = dot(originMinusCenter, vec) * 2.0f;
-    float C = dot(originMinusCenter, originMinusCenter) -
-              obj->worldBoundingSphere.w * obj->worldBoundingSphere.w;
-
-    float discriminant = B * B - 4.0f * C;
-    if (discriminant < 0.0f) {
-        return false;
-    }
-    discriminant = sqrt(discriminant);
-
-    float t0 = (-B - discriminant) * 0.5f;
-    float t1 = (-B + discriminant) * 0.5f;
-
-    if (t0 > t1) {
-        float temp = t0;
-        t0 = t1;
-        t1 = temp;
-    }
-
-    // The sphere is behind us
-    if (t1 < 0.0f) {
-        return false;
-    }
-    return true;
-}
-
-
-#endif // _TRANSFORM_DEF_
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
deleted file mode 100644
index 1d0b5be..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/transform.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.modelviewer)
-
-#include "scenegraph_objects.rsh"
-
-rs_script gTransformScript;
-
-typedef struct {
-    int changed;
-    rs_matrix4x4 *mat;
-} ParentData;
-
-//#define DEBUG_TRANSFORMS
-/* Unused function:
-static void debugTransform(SgTransform *data, const ParentData *parent) {
-    rsDebug("****** <Transform> ******", (int)data);
-    printName(data->name);
-    rsDebug("isDirty", data->isDirty);
-    rsDebug("parent", (int)parent);
-    rsDebug("child ", rsIsObject(data->children));
-
-    // Refresh matrices if dirty
-    if (data->isDirty && rsIsObject(data->components)) {
-        uint32_t numComponenets = rsAllocationGetDimX(data->components);
-        for (int i = 0; i < numComponenets; i ++) {
-            const SgTransformComponent *comp = NULL;
-            comp = (const SgTransformComponent *)rsGetElementAt(data->components, i);
-
-            if (rsIsObject(comp->name)) {
-                rsDebug((const char*)rsGetElementAt(comp->name, 0), comp->value);
-                rsDebug("Type", comp->type);
-            } else {
-                rsDebug("no name", comp->value);
-                rsDebug("Type", comp->type);
-            }
-        }
-    }
-
-    rsDebug("timestamp", data->timestamp);
-    rsDebug("****** </Transform> ******", (int)data);
-}
-*/
-
-static void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
-    rs_matrix4x4 temp;
-
-    switch (type) {
-    case TRANSFORM_TRANSLATE:
-        rsMatrixLoadTranslate(&temp, data.x, data.y, data.z);
-        break;
-    case TRANSFORM_ROTATE:
-        rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z);
-        break;
-    case TRANSFORM_SCALE:
-        rsMatrixLoadScale(&temp, data.x, data.y, data.z);
-        break;
-    }
-    rsMatrixMultiply(mat, &temp);
-}
-
-void root(const rs_allocation *v_in, rs_allocation *v_out, const void *usrData) {
-
-    SgTransform *data = (SgTransform *)rsGetElementAt(*v_in, 0);
-    const ParentData *parent = (const ParentData *)usrData;
-
-#ifdef DEBUG_TRANSFORMS
-    debugTransform(data, parent);
-#endif //DEBUG_TRANSFORMS
-
-    rs_matrix4x4 *localMat = &data->localMat;
-    rs_matrix4x4 *globalMat = &data->globalMat;
-
-    // Refresh matrices if dirty
-    if (data->isDirty && rsIsObject(data->components)) {
-        bool resetLocal = false;
-        uint32_t numComponenets = rsAllocationGetDimX(data->components);
-        for (int i = 0; i < numComponenets; i ++) {
-            if (!resetLocal) {
-                // Reset our local matrix only for component transforms
-                rsMatrixLoadIdentity(localMat);
-                resetLocal = true;
-            }
-            const SgTransformComponent *comp = NULL;
-            comp = (const SgTransformComponent *)rsGetElementAt(data->components, i);
-            appendTransformation(comp->type, comp->value, localMat);
-        }
-    }
-
-    if (parent) {
-        data->isDirty = (parent->changed || data->isDirty) ? 1 : 0;
-        if (data->isDirty) {
-            rsMatrixLoad(globalMat, parent->mat);
-            rsMatrixMultiply(globalMat, localMat);
-        }
-    } else if (data->isDirty) {
-        rsMatrixLoad(globalMat, localMat);
-    }
-
-    ParentData toChild;
-    toChild.changed = 0;
-    toChild.mat = globalMat;
-
-    if (data->isDirty) {
-        toChild.changed = 1;
-        data->timestamp ++;
-    }
-
-    if (rsIsObject(data->children)) {
-        rs_allocation nullAlloc = {0};
-        rsForEach(gTransformScript, data->children, nullAlloc, &toChild, sizeof(toChild));
-    }
-
-    data->isDirty = 0;
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs
deleted file mode 100644
index 88955a8..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/vertex_params.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.scenegraph)
-
-#include "scenegraph_objects.rsh"
-
-//#define DEBUG_PARAMS
-
-#include "params.rsh"
-
-void root(rs_allocation *v_out, const void *usrData) {
-    SgVertexShader *shader = (SgVertexShader *)rsGetElementAt(*v_out, 0);
-    const SgCamera *camera = (const SgCamera*)usrData;
-    processAllParams(shader->shaderConst, shader->shaderConstParams, camera);
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java
deleted file mode 100644
index 420e133..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FileSelector.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.app.ListActivity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-/**
- * A list view where the last item the user clicked is placed in
- * the "activated" state, causing its background to highlight.
- */
-public class FileSelector extends ListActivity {
-
-    File[] mCurrentSubList;
-    File mCurrentFile;
-
-    class DAEFilter implements FileFilter {
-        public boolean accept(File file) {
-            if (file.isDirectory()) {
-                return true;
-            }
-            return file.getName().endsWith(".dae");
-        }
-    }
-
-    private void populateList(File file) {
-
-        mCurrentFile = file;
-        setTitle(mCurrentFile.getAbsolutePath() + "/*.dae");
-        List<String> names = new ArrayList<String>();
-        names.add("..");
-
-        mCurrentSubList = mCurrentFile.listFiles(new DAEFilter());
-
-        if (mCurrentSubList != null) {
-            for (int i = 0; i < mCurrentSubList.length; i ++) {
-                String fileName = mCurrentSubList[i].getName();
-                if (mCurrentSubList[i].isDirectory()) {
-                    fileName = "/" + fileName;
-                }
-                names.add(fileName);
-            }
-        }
-
-        // Use the built-in layout for showing a list item with a single
-        // line of text whose background is changes when activated.
-        setListAdapter(new ArrayAdapter<String>(this,
-                android.R.layout.simple_list_item_activated_1, names));
-        getListView().setTextFilterEnabled(true);
-
-        // Tell the list view to show one checked/activated item at a time.
-        getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        populateList(new File("/sdcard/"));
-    }
-
-    @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
-        if (position == 0) {
-            File parent = mCurrentFile.getParentFile();
-            if (parent == null) {
-                return;
-            }
-            populateList(parent);
-            return;
-        }
-
-        // the first thing in list is parent directory
-        File selectedFile = mCurrentSubList[position - 1];
-        if (selectedFile.isDirectory()) {
-            populateList(selectedFile);
-            return;
-        }
-
-        Intent resultIntent = new Intent();
-        resultIntent.setData(Uri.fromFile(selectedFile));
-        setResult(RESULT_OK, resultIntent);
-        finish();
-    }
-
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java
deleted file mode 100644
index 28f916c..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/FullscreenBlur.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*

- * Copyright (C) 2011 The Android Open Source Project

- *

- * Licensed under the Apache License, Version 2.0 (the "License");

- * you may not use this file except in compliance with the License.

- * You may obtain a copy of the License at

- *

- *      http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing, software

- * distributed under the License is distributed on an "AS IS" BASIS,

- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

- * See the License for the specific language governing permissions and

- * limitations under the License.

- */

-

-

-package com.android.testapp;

-

-import java.util.ArrayList;

-

-import com.android.scenegraph.*;

-

-import android.content.res.Resources;

-import android.graphics.Bitmap;

-import android.graphics.BitmapFactory;

-import android.os.AsyncTask;

-import android.renderscript.*;

-import android.renderscript.Allocation.MipmapControl;

-import android.renderscript.Element.Builder;

-import android.renderscript.Font.Style;

-import android.renderscript.Program.TextureType;

-import android.renderscript.ProgramStore.DepthFunc;

-import android.util.Log;

-

-class FullscreenBlur {

-

-    static TextureRenderTarget sRenderTargetBlur0Color;

-    static TextureRenderTarget sRenderTargetBlur0Depth;

-    static TextureRenderTarget sRenderTargetBlur1Color;

-    static TextureRenderTarget sRenderTargetBlur1Depth;

-    static TextureRenderTarget sRenderTargetBlur2Color;

-    static TextureRenderTarget sRenderTargetBlur2Depth;

-

-    static FragmentShader mPF_BlurH;

-    static FragmentShader mPF_BlurV;

-    static FragmentShader mPF_SelectColor;

-    static FragmentShader mPF_Texture;

-    static VertexShader mPV_Paint;

-    static VertexShader mPV_Blur;

-

-    static int targetWidth;

-    static int targetHeight;

-

-    // This is only used when full screen blur is enabled

-    // Basically, it's the offscreen render targets

-    static void createRenderTargets(RenderScriptGL rs, int w, int h) {

-        targetWidth = w/8;

-        targetHeight = h/8;

-        Type.Builder b = new Type.Builder(rs, Element.RGBA_8888(rs));

-        Type renderType = b.setX(targetWidth).setY(targetHeight).create();

-        int usage = Allocation.USAGE_GRAPHICS_TEXTURE | Allocation.USAGE_GRAPHICS_RENDER_TARGET;

-        sRenderTargetBlur0Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

-        sRenderTargetBlur1Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

-        sRenderTargetBlur2Color = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

-

-        b = new Type.Builder(rs, Element.createPixel(rs, Element.DataType.UNSIGNED_16,

-                                                     Element.DataKind.PIXEL_DEPTH));

-        renderType = b.setX(targetWidth).setY(targetHeight).create();

-        usage = Allocation.USAGE_GRAPHICS_RENDER_TARGET;

-        sRenderTargetBlur0Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

-        sRenderTargetBlur1Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

-        sRenderTargetBlur2Depth = new TextureRenderTarget(Allocation.createTyped(rs, renderType, usage));

-    }

-

-    static void addOffsets(Renderable quad, float advance) {

-        quad.appendSourceParams(new Float4Param("blurOffset0", - advance * 2.5f));

-        quad.appendSourceParams(new Float4Param("blurOffset1", - advance * 0.5f));

-        quad.appendSourceParams(new Float4Param("blurOffset2", advance * 1.5f));

-        quad.appendSourceParams(new Float4Param("blurOffset3", advance * 3.5f));

-    }

-

-    static RenderPass addPass(Scene scene, Camera cam, TextureRenderTarget color, TextureRenderTarget depth) {

-        RenderPass pass = new RenderPass();

-        pass.setColorTarget(color);

-        pass.setDepthTarget(depth);

-        pass.setShouldClearColor(false);

-        pass.setShouldClearDepth(false);

-        pass.setCamera(cam);

-        scene.appendRenderPass(pass);

-        return pass;

-    }

-

-    static void addBlurPasses(Scene scene, RenderScriptGL rs, Camera cam) {

-        SceneManager sceneManager = SceneManager.getInstance();

-        ArrayList<RenderableBase> allDraw = scene.getRenderables();

-        int numDraw = allDraw.size();

-

-        ProgramRaster cullNone = ProgramRaster.CULL_NONE(rs);

-        ProgramStore blendAdd = SceneManager.BLEND_ADD_DEPTH_NONE(rs);

-        ProgramStore blendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(rs);

-

-        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture, blendAdd, cullNone);

-        RenderState selectCol = new RenderState(mPV_Blur, mPF_SelectColor, blendNone, cullNone);

-        RenderState hBlur = new RenderState(mPV_Blur, mPF_BlurH, blendNone, cullNone);

-        RenderState vBlur = new RenderState(mPV_Blur, mPF_BlurV, blendNone, cullNone);

-

-        // Renders the scene off screen

-        RenderPass blurSourcePass = addPass(scene, cam,

-                                            sRenderTargetBlur0Color,

-                                            sRenderTargetBlur0Depth);

-        blurSourcePass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));

-        blurSourcePass.setShouldClearColor(true);

-        blurSourcePass.setClearDepth(1.0f);

-        blurSourcePass.setShouldClearDepth(true);

-        for (int i = 0; i < numDraw; i ++) {

-            blurSourcePass.appendRenderable((Renderable)allDraw.get(i));

-        }

-

-        // Pass for selecting bright colors

-        RenderPass selectColorPass = addPass(scene, cam,

-                                             sRenderTargetBlur2Color,

-                                             sRenderTargetBlur2Depth);

-        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadS", selectCol);

-        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur0Color));

-        selectColorPass.appendRenderable(quad);

-

-        // Horizontal blur

-        RenderPass horizontalBlurPass = addPass(scene, cam,

-                                                sRenderTargetBlur1Color,

-                                                sRenderTargetBlur1Depth);

-        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadH", hBlur);

-        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));

-        addOffsets(quad, 1.0f / (float)targetWidth);

-        horizontalBlurPass.appendRenderable(quad);

-

-        // Vertical Blur

-        RenderPass verticalBlurPass = addPass(scene, cam,

-                                              sRenderTargetBlur2Color,

-                                              sRenderTargetBlur2Depth);

-        quad = sceneManager.getRenderableQuad("ScreenAlignedQuadV", vBlur);

-        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur1Color));

-        addOffsets(quad, 1.0f / (float)targetHeight);

-        verticalBlurPass.appendRenderable(quad);

-    }

-

-    // Additively renders the blurred colors on top of the scene

-    static void addCompositePass(Scene scene, RenderScriptGL rs, Camera cam) {

-        SceneManager sceneManager = SceneManager.getInstance();

-        RenderState drawTex = new RenderState(mPV_Blur, mPF_Texture,

-                                              SceneManager.BLEND_ADD_DEPTH_NONE(rs),

-                                              ProgramRaster.CULL_NONE(rs));

-

-        RenderPass compositePass = addPass(scene, cam, null, null);

-        Renderable quad = sceneManager.getRenderableQuad("ScreenAlignedQuadComposite", drawTex);

-        quad.appendSourceParams(new TextureParam("color", sRenderTargetBlur2Color));

-        compositePass.appendRenderable(quad);

-    }

-

-    static private FragmentShader getShader(Resources res, RenderScriptGL rs,

-                                            int resID, Type constants) {

-        FragmentShader.Builder fb = new FragmentShader.Builder(rs);

-        fb.setShader(res, resID);

-        fb.addTexture(TextureType.TEXTURE_2D, "color");

-        if (constants != null) {

-            fb.setObjectConst(constants);

-        }

-        FragmentShader prog = fb.create();

-        prog.getProgram().bindSampler(Sampler.CLAMP_LINEAR(rs), 0);

-        return prog;

-    }

-

-    static void initShaders(Resources res, RenderScriptGL rs) {

-        ScriptField_BlurOffsets blurConst = new ScriptField_BlurOffsets(rs, 1);

-        VertexShader.Builder vb = new VertexShader.Builder(rs);

-        vb.addInput(ScriptField_VertexShaderInputs.createElement(rs));

-        vb.setShader(res, R.raw.blur_vertex);

-        mPV_Blur = vb.create();

-

-        mPF_Texture = getShader(res, rs, R.raw.texture, null);

-        mPF_Texture.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(rs), 0);

-        mPF_BlurH = getShader(res, rs, R.raw.blur_h, blurConst.getAllocation().getType());

-        mPF_BlurV = getShader(res, rs, R.raw.blur_v, blurConst.getAllocation().getType());

-        mPF_SelectColor = getShader(res, rs, R.raw.select_color, null);

-    }

-

-}

-

-

-

-

-

diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java
deleted file mode 100644
index 314db80..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.Window;
-import android.view.Window;
-import android.net.Uri;
-
-import java.lang.Runtime;
-
-public class SimpleApp extends Activity {
-
-    private SimpleAppView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new SimpleAppView(this);
-        setContentView(mView);
-    }
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java
deleted file mode 100644
index fff6f34..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import com.android.scenegraph.*;
-import com.android.scenegraph.SceneManager.SceneLoadedCallback;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
-import android.renderscript.*;
-import android.renderscript.Program.TextureType;
-import android.util.Log;
-
-// This is where the scenegraph and the rendered objects are initialized and used
-public class SimpleAppRS {
-    SceneManager mSceneManager;
-
-    RenderScriptGL mRS;
-    Resources mRes;
-
-    Scene mScene;
-    Mesh mSimpleMesh;
-    Mesh mSphereMesh;
-    Mesh mCubeMesh;
-
-    public void init(RenderScriptGL rs, Resources res, int width, int height) {
-        mRS = rs;
-        mRes = res;
-        mSceneManager = SceneManager.getInstance();
-        mSceneManager.initRS(mRS, mRes, width, height);
-
-        mScene = new Scene();
-
-        setupGeometry();
-        setupColoredQuad();
-        setupTexturedQuad();
-        setupShadedGeometry();
-        setupCamera();
-        setupRenderPass();
-
-        mSceneManager.setActiveScene(mScene);
-
-        mScene.initRS();
-        mRS.bindRootScript(mSceneManager.getRenderLoop());
-    }
-
-    private void setupGeometry() {
-        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, 3,
-                                                         Mesh.TriangleMeshBuilder.TEXTURE_0);
-
-        // Create four vertices with texture coordinates
-        tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 0.0f);
-        tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 0.0f);
-        tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 0.0f);
-        tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 0.0f);
-
-        tmb.addTriangle(0, 1, 2);
-        tmb.addTriangle(2, 3, 0);
-        mSimpleMesh = tmb.create(true);
-
-        // Load a file that constains two pieces of geometry, a sphere and a cube
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.unit_obj);
-        for (int i = 0; i < model.getIndexEntryCount(); i ++) {
-            FileA3D.IndexEntry entry = model.getIndexEntry(i);
-            if (entry != null && entry.getName().equals("CubeMesh")) {
-                mCubeMesh = entry.getMesh();
-            } else if (entry != null && entry.getName().equals("SphereMesh")) {
-                mSphereMesh = entry.getMesh();
-            }
-        }
-    }
-
-    private void setupColoredQuad() {
-        // Built-in shader that provides position, texcoord and normal
-        VertexShader genericV = SceneManager.getDefaultVS();
-        // Built-in shader that displays a color
-        FragmentShader colorF = SceneManager.getColorFS();
-        RenderState colorRS = new RenderState(genericV, colorF, null, null);
-
-        // Draw a simple colored quad
-        Renderable quad = mScene.appendNewRenderable();
-        quad.setMesh(mSimpleMesh);
-        // Our shader has a constant input called "color"
-        // This tells the scenegraph to assign the following float3 to that input
-        quad.appendSourceParams(new Float4Param("color", 0.2f, 0.3f, 0.4f));
-        quad.setRenderState(colorRS);
-    }
-
-    private void setupTexturedQuad() {
-        // Built-in shader that provides position, texcoord and normal
-        VertexShader genericV = SceneManager.getDefaultVS();
-        // Built-in shader that displays a texture
-        FragmentShader textureF = SceneManager.getTextureFS();
-        // We want to use transparency based on the alpha channel of the texture
-        ProgramStore alphaBlend = ProgramStore.BLEND_ALPHA_DEPTH_TEST(mRS);
-        RenderState texRS = new RenderState(genericV, textureF, alphaBlend, null);
-
-        // Draw a textured quad
-        Renderable quad = mScene.appendNewRenderable();
-        quad.setMesh(mSimpleMesh);
-        // Make a transform to position the quad
-        CompoundTransform t = mScene.appendNewCompoundTransform();
-        t.addTranslate("position", new Float3(2, 2, 0));
-        quad.setTransform(t);
-        // Our fragment shader has a constant texture input called "color"
-        // This will assign an icon from drawables to that input
-        quad.appendSourceParams(new TextureParam("color", new Texture2D(R.drawable.icon)));
-        quad.setRenderState(texRS);
-    }
-
-    private FragmentShader createLambertShader() {
-        // Describe what constant inputs our shader wants
-        Element.Builder b = new Element.Builder(mRS);
-        b.add(Element.F32_4(mRS), "cameraPos");
-
-        // Create a shader from a text file in resources
-        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
-        // Tell the shader what constants we want
-        fb.setShaderConst(new Type.Builder(mRS, b.create()).setX(1).create());
-        // Shader code location
-        fb.setShader(mRes, R.raw.diffuse);
-        // We want a texture called diffuse on our shader
-        fb.addTexture(TextureType.TEXTURE_2D, "diffuse");
-        FragmentShader shader = fb.create();
-        mScene.appendShader(shader);
-        return shader;
-    }
-
-    private void setupShadedGeometry() {
-        // Built-in shader that provides position, texcoord and normal
-        VertexShader genericV = SceneManager.getDefaultVS();
-        // Custom shader
-        FragmentShader diffuseF = createLambertShader();
-        RenderState diffuseRS = new RenderState(genericV, diffuseF, null, null);
-
-        // Draw a sphere
-        Renderable sphere = mScene.appendNewRenderable();
-        // Use the sphere geometry loaded earlier
-        sphere.setMesh(mSphereMesh);
-        // Make a transform to position the sphere
-        CompoundTransform t = mScene.appendNewCompoundTransform();
-        t.addTranslate("position", new Float3(-1, 2, 3));
-        t.addScale("scale", new Float3(1.4f, 1.4f, 1.4f));
-        sphere.setTransform(t);
-        // Tell the renderable which texture to use when we draw
-        // This will mean a texture param in the shader called "diffuse"
-        // will be assigned a texture called red.jpg
-        sphere.appendSourceParams(new TextureParam("diffuse", new Texture2D("", "red.jpg")));
-        sphere.setRenderState(diffuseRS);
-
-        // Draw a cube
-        Renderable cube = mScene.appendNewRenderable();
-        cube.setMesh(mCubeMesh);
-        t = mScene.appendNewCompoundTransform();
-        t.addTranslate("position", new Float3(-2, -2.1f, 0));
-        t.addRotate("rotateX", new Float3(1, 0, 0), 30);
-        t.addRotate("rotateY", new Float3(0, 1, 0), 30);
-        t.addScale("scale", new Float3(2, 2, 2));
-        cube.setTransform(t);
-        cube.appendSourceParams(new TextureParam("diffuse", new Texture2D("", "orange.jpg")));
-        cube.setRenderState(diffuseRS);
-    }
-
-    private void setupCamera() {
-        Camera camera = mScene.appendNewCamera();
-        camera.setFar(200);
-        camera.setNear(0.1f);
-        camera.setFOV(60);
-        CompoundTransform cameraTransform = mScene.appendNewCompoundTransform();
-        cameraTransform.addTranslate("camera", new Float3(0, 0, 10));
-        camera.setTransform(cameraTransform);
-    }
-
-    private void setupRenderPass() {
-        RenderPass mainPass = mScene.appendNewRenderPass();
-        mainPass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));
-        mainPass.setShouldClearColor(true);
-        mainPass.setClearDepth(1.0f);
-        mainPass.setShouldClearDepth(true);
-        mainPass.setCamera(mScene.getCameras().get(0));
-        ArrayList<RenderableBase> allRender = mScene.getRenderables();
-        for (RenderableBase renderable : allRender) {
-            mainPass.appendRenderable((Renderable)renderable);
-        }
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java
deleted file mode 100644
index 2112181..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-public class SimpleAppView extends RSSurfaceView {
-
-    public SimpleAppView(Context context) {
-        super(context);
-    }
-
-    private RenderScriptGL mRS;
-    SimpleAppRS mRender;
-
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            sc.setSamples(1, 2, 1);
-            mRS = createRenderScriptGL(sc);
-            mRS.setSurface(holder, w, h);
-            mRender = new SimpleAppRS();
-            mRender.init(mRS, getResources(), w, h);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (mRS != null) {
-            mRender = null;
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java
deleted file mode 100644
index 385a7ab..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestApp.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-import android.view.MenuInflater;
-import android.view.Window;
-import android.net.Uri;
-
-import java.lang.Runtime;
-
-public class TestApp extends Activity {
-
-    private TestAppView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new TestAppView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.pause();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.loader_menu, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        // Handle item selection
-        switch (item.getItemId()) {
-        case R.id.load_model:
-            loadModel();
-            return true;
-        case R.id.use_blur:
-            mView.mRender.toggleBlur();
-            return true;
-        default:
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private static final int FIND_DAE_MODEL = 10;
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (resultCode == RESULT_OK) {
-            if (requestCode == FIND_DAE_MODEL) {
-                Uri selectedImageUri = data.getData();
-                Log.e("Selected Path: ", selectedImageUri.getPath());
-                mView.mRender.loadModel(selectedImageUri.getPath());
-            }
-        }
-    }
-
-    public void loadModel() {
-        Intent intent = new Intent();
-        intent.setAction(Intent.ACTION_PICK);
-        intent.setClassName("com.android.testapp",
-                            "com.android.testapp.FileSelector");
-        startActivityForResult(intent, FIND_DAE_MODEL);
-    }
-
-}
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java
deleted file mode 100644
index 5bd8f0b..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppLoadingScreen.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import com.android.scenegraph.SceneManager;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
-import android.renderscript.*;
-import android.renderscript.Allocation.MipmapControl;
-import android.renderscript.Element.Builder;
-import android.renderscript.Font.Style;
-import android.renderscript.Program.TextureType;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.util.Log;
-
-// This is where the scenegraph and the rendered objects are initialized and used
-public class TestAppLoadingScreen {
-
-    private static String TAG = "TestAppLoadingScreen";
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private ScriptC_test_app mScript;
-
-    public TestAppLoadingScreen(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        // Shows the loading screen with some text
-        renderLoading();
-        // Adds a little 3D bugdroid model to the laoding screen asynchronously.
-        new LoadingScreenLoaderTask().execute();
-    }
-
-    public void showLoadingScreen(boolean show) {
-        if (show) {
-            mRS.bindRootScript(mScript);
-        } else {
-            mRS.bindRootScript(SceneManager.getInstance().getRenderLoop());
-        }
-    }
-
-    // The loading screen has some elements that shouldn't be loaded on the UI thread
-    private class LoadingScreenLoaderTask extends AsyncTask<String, Void, Boolean> {
-        Allocation robotTex;
-        Mesh robotMesh;
-        protected Boolean doInBackground(String... names) {
-            long start = System.currentTimeMillis();
-            robotTex = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
-                                                           MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                                                           Allocation.USAGE_GRAPHICS_TEXTURE);
-
-            FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
-            FileA3D.IndexEntry entry = model.getIndexEntry(0);
-            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                robotMesh = entry.getMesh();
-            }
-
-            mScript.set_gPFSBackground(ProgramStore.BLEND_NONE_DEPTH_TEST(mRS));
-
-            ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
-            b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                         ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-            ProgramFragment pfDefault = b.create();
-            pfDefault.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
-            mScript.set_gPFBackground(pfDefault);
-
-            ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-            ProgramVertexFixedFunction pvDefault = pvb.create();
-            ProgramVertexFixedFunction.Constants va = new ProgramVertexFixedFunction.Constants(mRS);
-            ((ProgramVertexFixedFunction)pvDefault).bindConstants(va);
-            mScript.set_gPVBackground(pvDefault);
-
-            long end = System.currentTimeMillis();
-            Log.v("TIMER", "Loading load time: " + (end - start));
-            return new Boolean(true);
-        }
-
-        protected void onPostExecute(Boolean result) {
-            mScript.set_gRobotTex(robotTex);
-            mScript.set_gRobotMesh(robotMesh);
-        }
-    }
-
-    // Creates a simple script to show a loding screen until everything is initialized
-    // Could also be used to do some custom renderscript work before handing things over
-    // to the scenegraph
-    void renderLoading() {
-        mScript = new ScriptC_test_app(mRS, mRes, R.raw.test_app);
-        mRS.bindRootScript(mScript);
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
deleted file mode 100644
index 3aa80f4..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2011-2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-import com.android.scenegraph.*;
-import com.android.scenegraph.SceneManager.SceneLoadedCallback;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
-import android.renderscript.*;
-import android.renderscript.Program.TextureType;
-import android.util.Log;
-
-// This is where the scenegraph and the rendered objects are initialized and used
-public class TestAppRS {
-
-    private static String modelName = "orientation_test.dae";
-    private static String TAG = "TestAppRS";
-    private static String mFilePath = "";
-
-    int mWidth;
-    int mHeight;
-
-    boolean mUseBlur;
-
-    TestAppLoadingScreen mLoadingScreen;
-
-    // Used to asynchronously load scene elements like meshes and transform hierarchies
-    SceneLoadedCallback mLoadedCallback = new SceneLoadedCallback() {
-        public void run() {
-            prepareToRender(mLoadedScene);
-        }
-    };
-
-    // Top level class that initializes all the elements needed to use the scene graph
-    SceneManager mSceneManager;
-
-    // Used to move the camera around in the 3D world
-    TouchHandler mTouchHandler;
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-
-    // Shaders
-    private FragmentShader mPaintF;
-    private FragmentShader mLightsF;
-    private FragmentShader mLightsDiffF;
-    private FragmentShader mAluminumF;
-    private FragmentShader mPlasticF;
-    private FragmentShader mDiffuseF;
-    private FragmentShader mTextureF;
-    private VertexShader mGenericV;
-
-    Scene mActiveScene;
-
-    // This is a part of the test app, it's used to tests multiple render passes and is toggled
-    // on and off in the menu, off by default
-    void toggleBlur() {
-        mUseBlur = !mUseBlur;
-
-        mActiveScene.clearRenderPasses();
-        initRenderPasses();
-        mActiveScene.initRenderPassRS(mRS, mSceneManager);
-
-        // This is just a hardcoded object in the scene that gets turned on and off for the demo
-        // to make things look a bit better. This could be deleted in the cleanup
-        Renderable plane = (Renderable)mActiveScene.getRenderableByName("pPlaneShape1");
-        if (plane != null) {
-            plane.setVisible(!mUseBlur);
-        }
-    }
-
-    public void init(RenderScriptGL rs, Resources res, int width, int height) {
-        mUseBlur = false;
-        mRS = rs;
-        mRes = res;
-        mWidth = width;
-        mHeight = height;
-
-        mTouchHandler = new TouchHandler();
-
-        mSceneManager = SceneManager.getInstance();
-        // Initializes all the RS specific scenegraph elements
-        mSceneManager.initRS(mRS, mRes, mWidth, mHeight);
-
-        mLoadingScreen = new TestAppLoadingScreen(mRS, mRes);
-
-        // Initi renderscript stuff specific to the app. This will need to be abstracted out later.
-        FullscreenBlur.createRenderTargets(mRS, mWidth, mHeight);
-        initPaintShaders();
-
-        // Load a scene to render
-        mSceneManager.loadModel(mFilePath + modelName, mLoadedCallback);
-    }
-
-    // When a new model file is selected from the UI, this function gets called to init everything
-    void loadModel(String path) {
-        mLoadingScreen.showLoadingScreen(true);
-        mActiveScene.destroyRS();
-        mSceneManager.loadModel(path, mLoadedCallback);
-    }
-
-    public void onActionDown(float x, float y) {
-        mTouchHandler.onActionDown(x, y);
-    }
-
-    public void onActionScale(float scale) {
-        mTouchHandler.onActionScale(scale);
-    }
-
-    public void onActionMove(float x, float y) {
-        mTouchHandler.onActionMove(x, y);
-    }
-
-    FragmentShader createFromResource(int id, boolean addCubemap, Type constType) {
-        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
-        fb.setShaderConst(constType);
-        fb.setShader(mRes, id);
-        fb.addTexture(TextureType.TEXTURE_2D, "diffuse");
-        if (addCubemap) {
-            fb.addShaderTexture(TextureType.TEXTURE_CUBE, "reflection");
-        }
-        FragmentShader pf = fb.create();
-        pf.getProgram().bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0);
-        if (addCubemap) {
-            pf.getProgram().bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(mRS), 1);
-        }
-        return pf;
-    }
-
-    private void initPaintShaders() {
-        mGenericV = SceneManager.getDefaultVS();
-
-        ScriptField_CameraParams camParams = new ScriptField_CameraParams(mRS, 1);
-        Type camParamType = camParams.getAllocation().getType();
-        ScriptField_LightParams lightParams = new ScriptField_LightParams(mRS, 1);
-
-        mPaintF = createFromResource(R.raw.paintf, true, camParamType);
-        // Assign a reflection map
-        TextureCube envCube = new TextureCube("sdcard/scenegraph/", "cube_env.png");
-        mPaintF.appendSourceParams(new TextureParam("reflection", envCube));
-
-        mAluminumF = createFromResource(R.raw.metal, true, camParamType);
-        TextureCube diffCube = new TextureCube("sdcard/scenegraph/", "cube_spec.png");
-        mAluminumF.appendSourceParams(new TextureParam("reflection", diffCube));
-
-        mPlasticF = createFromResource(R.raw.plastic, false, camParamType);
-        mDiffuseF = createFromResource(R.raw.diffuse, false, camParamType);
-        mTextureF = SceneManager.getTextureFS();
-
-        FragmentShader.Builder fb = new FragmentShader.Builder(mRS);
-        fb.setObjectConst(lightParams.getAllocation().getType());
-        fb.setShader(mRes, R.raw.plastic_lights);
-        mLightsF = fb.create();
-
-        fb = new FragmentShader.Builder(mRS);
-        fb.setObjectConst(lightParams.getAllocation().getType());
-        fb.setShader(mRes, R.raw.diffuse_lights);
-        mLightsDiffF = fb.create();
-
-        FullscreenBlur.initShaders(mRes, mRS);
-    }
-
-    void initRenderPasses() {
-        ArrayList<RenderableBase> allDraw = mActiveScene.getRenderables();
-        int numDraw = allDraw.size();
-
-        if (mUseBlur) {
-            FullscreenBlur.addBlurPasses(mActiveScene, mRS, mTouchHandler.getCamera());
-        }
-
-        RenderPass mainPass = new RenderPass();
-        mainPass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f));
-        mainPass.setShouldClearColor(true);
-        mainPass.setClearDepth(1.0f);
-        mainPass.setShouldClearDepth(true);
-        mainPass.setCamera(mTouchHandler.getCamera());
-        for (int i = 0; i < numDraw; i ++) {
-            mainPass.appendRenderable((Renderable)allDraw.get(i));
-        }
-        mActiveScene.appendRenderPass(mainPass);
-
-        if (mUseBlur) {
-            FullscreenBlur.addCompositePass(mActiveScene, mRS, mTouchHandler.getCamera());
-        }
-    }
-
-    private void addShadersToScene() {
-        mActiveScene.appendShader(mPaintF);
-        mActiveScene.appendShader(mLightsF);
-        mActiveScene.appendShader(mLightsDiffF);
-        mActiveScene.appendShader(mAluminumF);
-        mActiveScene.appendShader(mPlasticF);
-        mActiveScene.appendShader(mDiffuseF);
-        mActiveScene.appendShader(mTextureF);
-    }
-
-    public void prepareToRender(Scene s) {
-        mSceneManager.setActiveScene(s);
-        mActiveScene = s;
-        mTouchHandler.init(mActiveScene);
-        addShadersToScene();
-        RenderState plastic = new RenderState(mGenericV, mPlasticF, null, null);
-        RenderState diffuse = new RenderState(mGenericV, mDiffuseF, null, null);
-        RenderState paint = new RenderState(mGenericV, mPaintF, null, null);
-        RenderState aluminum = new RenderState(mGenericV, mAluminumF, null, null);
-        RenderState lights = new RenderState(mGenericV, mLightsF, null, null);
-        RenderState diff_lights = new RenderState(mGenericV, mLightsDiffF, null, null);
-        RenderState diff_lights_no_cull = new RenderState(mGenericV, mLightsDiffF, null,
-                                                          ProgramRaster.CULL_NONE(mRS));
-        RenderState glassTransp = new RenderState(mGenericV, mPaintF,
-                                                  ProgramStore.BLEND_ALPHA_DEPTH_TEST(mRS), null);
-        RenderState texState = new RenderState(mGenericV, mTextureF, null, null);
-
-        initRenderPasses();
-
-        mActiveScene.assignRenderState(plastic);
-
-        mActiveScene.assignRenderStateToMaterial(diffuse, "lambert2$");
-
-        mActiveScene.assignRenderStateToMaterial(paint, "^Paint");
-        mActiveScene.assignRenderStateToMaterial(paint, "^Carbon");
-        mActiveScene.assignRenderStateToMaterial(paint, "^Glass");
-        mActiveScene.assignRenderStateToMaterial(paint, "^MainGlass");
-
-        mActiveScene.assignRenderStateToMaterial(aluminum, "^Metal");
-        mActiveScene.assignRenderStateToMaterial(aluminum, "^Brake");
-
-        mActiveScene.assignRenderStateToMaterial(glassTransp, "^GlassLight");
-
-        mActiveScene.assignRenderStateToMaterial(lights, "^LightBlinn");
-        mActiveScene.assignRenderStateToMaterial(diff_lights, "^LightLambert");
-        mActiveScene.assignRenderStateToMaterial(diff_lights_no_cull, "^LightLambertNoCull");
-        mActiveScene.assignRenderStateToMaterial(texState, "^TextureOnly");
-
-        Renderable plane = (Renderable)mActiveScene.getRenderableByName("pPlaneShape1");
-        if (plane != null) {
-            plane.setRenderState(texState);
-            plane.setVisible(!mUseBlur);
-        }
-
-        long start = System.currentTimeMillis();
-        mActiveScene.initRS();
-        long end = System.currentTimeMillis();
-        Log.v("TIMER", "Scene init time: " + (end - start));
-
-        mLoadingScreen.showLoadingScreen(false);
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java
deleted file mode 100644
index 33ca1b8..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppView.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-
-public class TestAppView extends RSSurfaceView {
-
-    public TestAppView(Context context) {
-        super(context);
-        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
-    }
-
-    private RenderScriptGL mRS;
-    TestAppRS mRender;
-
-    private ScaleGestureDetector mScaleDetector;
-    private static final int INVALID_POINTER_ID = -1;
-    private int mActivePointerId = INVALID_POINTER_ID;
-
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            sc.setSamples(1, 2, 1);
-            mRS = createRenderScriptGL(sc);
-            mRS.setSurface(holder, w, h);
-            mRender = new TestAppRS();
-            mRender.init(mRS, getResources(), w, h);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (mRS != null) {
-            mRender = null;
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event)
-    {
-        // break point at here
-        // this method doesn't work when 'extends View' include 'extends ScrollView'.
-        return super.onKeyDown(keyCode, event);
-    }
-
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mScaleDetector.onTouchEvent(ev);
-
-        boolean ret = false;
-        float x = ev.getX();
-        float y = ev.getY();
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN: {
-            mRender.onActionDown(x, y);
-            mActivePointerId = ev.getPointerId(0);
-            ret = true;
-            break;
-        }
-        case MotionEvent.ACTION_MOVE: {
-            if (!mScaleDetector.isInProgress()) {
-                mRender.onActionMove(x, y);
-            }
-            mRender.onActionDown(x, y);
-            ret = true;
-            break;
-        }
-
-        case MotionEvent.ACTION_UP: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_CANCEL: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_POINTER_UP: {
-            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-            final int pointerId = ev.getPointerId(pointerIndex);
-            if (pointerId == mActivePointerId) {
-                // This was our active pointer going up. Choose a new
-                // active pointer and adjust accordingly.
-                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-                x = ev.getX(newPointerIndex);
-                y = ev.getY(newPointerIndex);
-                mRender.onActionDown(x, y);
-                mActivePointerId = ev.getPointerId(newPointerIndex);
-            }
-            break;
-        }
-        }
-
-        return ret;
-    }
-
-    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-        @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            mRender.onActionScale(detector.getScaleFactor());
-            return true;
-        }
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
deleted file mode 100644
index d0f9797..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.testapp;
-
-import android.util.Log;
-import android.renderscript.Float3;
-import com.android.scenegraph.*;
-import com.android.scenegraph.CompoundTransform.RotateComponent;
-import com.android.scenegraph.CompoundTransform.TranslateComponent;
-
-public class TouchHandler {
-    private static String TAG = "TouchHandler";
-
-    float mLastX;
-    float mLastY;
-
-    float mRotateXValue;
-    float mRotateYValue;
-    Float3 mDistValue;
-    Float3 mPosValue;
-
-    CompoundTransform mCameraRig;
-    RotateComponent mRotateX;
-    RotateComponent mRotateY;
-    TranslateComponent mDist;
-    TranslateComponent mPosition;
-    Camera mCamera;
-
-    public void init(Scene scene) {
-        // Some initial values for camera position
-        mRotateXValue = -20;
-        mRotateYValue = 0;
-        mDistValue = new Float3(0, 0, 45);
-        mPosValue = new Float3(0, 4, 0);
-
-        // Make a camera transform we can manipulate
-        mCameraRig = scene.appendNewCompoundTransform();
-        mCameraRig.setName("CameraRig");
-
-        mPosition = mCameraRig.addTranslate("Position", mPosValue);
-        mRotateY  = mCameraRig.addRotate("RotateY", new Float3(0, 1, 0), mRotateYValue);
-        mRotateX  = mCameraRig.addRotate("RotateX", new Float3(1, 0, 0), mRotateXValue);
-        mDist     = mCameraRig.addTranslate("Distance", mDistValue);
-
-        mCamera = scene.appendNewCamera();
-        mCamera.setTransform(mCameraRig);
-    }
-
-    public Camera getCamera() {
-        return mCamera;
-    }
-
-    public void onActionDown(float x, float y) {
-        mLastX = x;
-        mLastY = y;
-    }
-
-    public void onActionScale(float scale) {
-        if (mDist == null) {
-            return;
-        }
-        mDistValue.z *= 1.0f / scale;
-        mDistValue.z = Math.max(10.0f, Math.min(mDistValue.z, 150.0f));
-        mDist.setValue(mDistValue);
-    }
-
-    public void onActionMove(float x, float y) {
-        if (mRotateX == null) {
-            return;
-        }
-
-        float dx = mLastX - x;
-        float dy = mLastY - y;
-
-        if (Math.abs(dy) <= 2.0f) {
-            dy = 0.0f;
-        }
-        if (Math.abs(dx) <= 2.0f) {
-            dx = 0.0f;
-        }
-
-        mRotateYValue += dx * 0.25f;
-        mRotateYValue %= 360.0f;
-
-        mRotateXValue  += dy * 0.25f;
-        mRotateXValue  = Math.max(mRotateXValue , -80.0f);
-        mRotateXValue  = Math.min(mRotateXValue , 0.0f);
-
-        mRotateX.setAngle(mRotateXValue);
-        mRotateY.setAngle(mRotateYValue);
-
-        mLastX = x;
-        mLastY = y;
-    }
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
deleted file mode 100644
index d94da52..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.testapp)
-
-#include "rs_graphics.rsh"
-#include "test_app.rsh"
-
-// Making sure these get reflected
-FBlurOffsets *blurExport;
-VShaderInputs *iExport;
-FShaderParams *fConst;
-FShaderLightParams *fConts2;
-VSParams *vConst2;
-VObjectParams *vConst3;
-
-rs_program_vertex gPVBackground;
-rs_program_fragment gPFBackground;
-
-rs_allocation gRobotTex;
-rs_mesh gRobotMesh;
-
-rs_program_store gPFSBackground;
-
-float gRotate;
-
-void init() {
-    gRotate = 0.0f;
-}
-
-static float gRotateY = 120.0f;
-static float gZoom = 50.0f;
-static void displayLoading() {
-    if (rsIsObject(gRobotTex) && rsIsObject(gRobotMesh)) {
-        rsgBindProgramVertex(gPVBackground);
-        rs_matrix4x4 proj;
-        float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
-        rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 100.0f);
-        rsgProgramVertexLoadProjectionMatrix(&proj);
-
-        rsgBindProgramFragment(gPFBackground);
-        rsgBindProgramStore(gPFSBackground);
-        rsgBindTexture(gPFBackground, 0, gRobotTex);
-
-        rs_matrix4x4 matrix;
-        rsMatrixLoadIdentity(&matrix);
-        // Position our models on the screen
-        gRotateY += rsGetDt()*100;
-        rsMatrixTranslate(&matrix, 0, 0, -gZoom);
-        rsMatrixRotate(&matrix, 20.0f, 1.0f, 0.0f, 0.0f);
-        rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-        rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f);
-        rsgProgramVertexLoadModelMatrix(&matrix);
-        rsgDrawMesh(gRobotMesh);
-    }
-
-    uint width = rsgGetWidth();
-    uint height = rsgGetHeight();
-    int left = 0, right = 0, top = 0, bottom = 0;
-    const char* text = "Initializing...";
-    rsgMeasureText(text, &left, &right, &top, &bottom);
-    int centeredPos = width / 2 - (right - left) / 2;
-    rsgDrawText(text, centeredPos, height / 2 + height / 10);
-}
-
-int root(void) {
-    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgClearDepth(1.0f);
-    displayLoading();
-    return 30;
-}
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh
deleted file mode 100644
index 5fbcbb2..0000000
--- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/test_app.rsh
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.testapp)
-
-// Helpers
-typedef struct ViewProjParams {
-    rs_matrix4x4 viewProj;
-} VSParams;
-
-typedef struct ModelParams {
-    rs_matrix4x4 model;
-} VObjectParams;
-
-typedef struct CameraParams {
-    float4 cameraPos;
-} FShaderParams;
-
-typedef struct LightParams {
-    float4 lightPos_0;
-    float4 lightColor_0;
-    float4 lightPos_1;
-    float4 lightColor_1;
-    float4 cameraPos;
-    float4 diffuse;
-} FShaderLightParams;
-
-typedef struct BlurOffsets {
-    float blurOffset0;
-    float blurOffset1;
-    float blurOffset2;
-    float blurOffset3;
-} FBlurOffsets;
-
-typedef struct VertexShaderInputs {
-    float4 position;
-    float3 normal;
-    float2 texture0;
-} VShaderInputs;
diff --git a/tests/RenderScriptTests/ShadersTest/Android.mk b/tests/RenderScriptTests/ShadersTest/Android.mk
deleted file mode 100644
index fb6356e..0000000
--- a/tests/RenderScriptTests/ShadersTest/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_SDK_VERSION := 17
-
-LOCAL_PACKAGE_NAME := ShadersTest
-
-include $(BUILD_PACKAGE)
diff --git a/tests/RenderScriptTests/ShadersTest/AndroidManifest.xml b/tests/RenderScriptTests/ShadersTest/AndroidManifest.xml
deleted file mode 100644
index 871200d..0000000
--- a/tests/RenderScriptTests/ShadersTest/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.shaderstest">
-
-    <uses-permission android:name="android.permission.INTERNET" />
-    
-    <application android:label="_ShadersTest">
-        <activity android:name="ShadersTest"
-                  android:label="_ShadersTest"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.png b/tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.png
deleted file mode 100644
index f7353fd..0000000
--- a/tests/RenderScriptTests/ShadersTest/res/drawable-nodpi/robot.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl b/tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl
deleted file mode 100644
index 096843b..0000000
--- a/tests/RenderScriptTests/ShadersTest/res/raw/depth_fs.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-void main() {
-    // Non-linear depth value
-    float z = gl_FragCoord.z;
-    // Near and far planes from the projection
-    // In practice, these values can be used to tweak
-    // the focus range
-    float n = UNI_near;
-    float f = UNI_far;
-    // Linear depth value
-    z = (2.0 * n) / (f + n - z * (f - n));
-
-    gl_FragColor = vec4(z, z, z, 1.0);
-}
diff --git a/tests/RenderScriptTests/ShadersTest/res/raw/robot.a3d b/tests/RenderScriptTests/ShadersTest/res/raw/robot.a3d
deleted file mode 100644
index f48895c..0000000
--- a/tests/RenderScriptTests/ShadersTest/res/raw/robot.a3d
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl b/tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl
deleted file mode 100644
index 2dc1ea3..0000000
--- a/tests/RenderScriptTests/ShadersTest/res/raw/vignette_fs.glsl
+++ /dev/null
@@ -1,31 +0,0 @@
-#define CRT_MASK
-
-varying vec2 varTex0;
-
-void main() {
-    lowp vec4 color = texture2D(UNI_Tex0, varTex0);
-    
-    vec2 powers = pow(abs((gl_FragCoord.xy / vec2(UNI_width, UNI_height)) - 0.5), vec2(2.0));
-    float gradient = smoothstep(UNI_size - UNI_feather, UNI_size + UNI_feather,
-            powers.x + powers.y);
-
-    color = vec4(mix(color.rgb, vec3(0.0), gradient), 1.0);
-
-#ifdef CRT_MASK
-    float vShift = gl_FragCoord.y;
-    if (mod(gl_FragCoord.x, 6.0) >= 3.0) {
-        vShift += 2.0;
-    }
-
-    lowp vec3 r = vec3(0.95, 0.0, 0.2);
-    lowp vec3 g = vec3(0.2, 0.95, 0.0);
-    lowp vec3 b = vec3(0.0, 0.2, 0.95);
-    int channel = int(floor(mod(gl_FragCoord.x, 3.0)));
-    lowp vec4 crt = vec4(r[channel], g[channel], b[channel], 1.0);
-    crt *= clamp(floor(mod(vShift, 4.0)), 0.0, 1.0);
-    
-    color = (crt * color * 1.25) + 0.05;
-#endif
-
-    gl_FragColor = color;
-}
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java
deleted file mode 100644
index 6803fbb..0000000
--- a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.shaderstest;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-@SuppressWarnings({"UnusedDeclaration"})
-public class ShadersTest extends Activity {
-
-    private ShadersTestView mView;
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mView = new ShadersTestView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mView.resume();
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mView.pause();
-    }
-}
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java
deleted file mode 100644
index dad97e2..0000000
--- a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestRS.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.shaderstest;
-
-import android.content.res.Resources;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.Element.DataKind;
-import android.renderscript.Element.DataType;
-import android.renderscript.FileA3D;
-import android.renderscript.Mesh;
-import android.renderscript.Program;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramFragmentFixedFunction;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramStore.DepthFunc;
-import android.renderscript.ProgramVertex;
-import android.renderscript.ProgramVertexFixedFunction;
-import android.renderscript.RSRuntimeException;
-import android.renderscript.RenderScriptGL;
-import android.renderscript.Sampler;
-import android.renderscript.Type.Builder;
-
-@SuppressWarnings({"FieldCanBeLocal"})
-public class ShadersTestRS {
-    public ShadersTestRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    public void surfaceChanged() {
-        initBuffers(mRS.getWidth(), mRS.getHeight());
-    }
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Sampler mLinearClamp;
-    private Sampler mNearestClamp;
-    private ProgramStore mPSBackground;
-    private ProgramFragment mPFBackground;
-    private ProgramVertex mPVBackground;
-    private ProgramVertexFixedFunction.Constants mPVA;
-
-    private ProgramFragment mPFVignette;
-    private ScriptField_VignetteConstants_s mFSVignetteConst;
-
-    private Allocation mMeshTexture;
-    private Allocation mScreen;
-    private Allocation mScreenDepth;
-
-    private ScriptField_MeshInfo mMeshes;
-    private ScriptC_shaderstest mScript;
-
-
-    public void onActionDown(float x, float y) {
-        mScript.invoke_onActionDown(x, y);
-    }
-
-    public void onActionScale(float scale) {
-        mScript.invoke_onActionScale(scale);
-    }
-
-    public void onActionMove(float x, float y) {
-        mScript.invoke_onActionMove(x, y);
-    }
-
-    private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
-
-        b.setDepthFunc(DepthFunc.LESS);
-        b.setDitherEnabled(false);
-        b.setDepthMaskEnabled(true);
-        mPSBackground = b.create();
-
-        mScript.set_gPFSBackground(mPSBackground);
-    }
-
-    private void initPF() {
-        mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
-        mScript.set_gLinear(mLinearClamp);
-
-        mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
-        mScript.set_gNearest(mNearestClamp);
-
-        ProgramFragmentFixedFunction.Builder b = new ProgramFragmentFixedFunction.Builder(mRS);
-        b.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE,
-                     ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
-        mPFBackground = b.create();
-        mPFBackground.bindSampler(mLinearClamp, 0);
-        mScript.set_gPFBackground(mPFBackground);
-
-        mFSVignetteConst = new ScriptField_VignetteConstants_s(mRS, 1);
-        mScript.bind_gFSVignetteConstants(mFSVignetteConst);
-
-        ProgramFragment.Builder fs;
-
-        fs = new ProgramFragment.Builder(mRS);
-        fs.setShader(mRes, R.raw.vignette_fs);
-        fs.addConstant(mFSVignetteConst.getAllocation().getType());
-        fs.addTexture(Program.TextureType.TEXTURE_2D);
-        mPFVignette = fs.create();
-        mPFVignette.bindConstants(mFSVignetteConst.getAllocation(), 0);
-        mScript.set_gPFVignette(mPFVignette);
-    }
-
-    private void initPV() {
-        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
-        mPVBackground = pvb.create();
-
-        mPVA = new ProgramVertexFixedFunction.Constants(mRS);
-        ((ProgramVertexFixedFunction) mPVBackground).bindConstants(mPVA);
-
-        mScript.set_gPVBackground(mPVBackground);
-    }
-
-    private void loadImage() {
-        mMeshTexture = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot,
-                Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE,
-                Allocation.USAGE_GRAPHICS_TEXTURE);
-        mScript.set_gTMesh(mMeshTexture);
-    }
-
-    private void initMeshes(FileA3D model) {
-        int numEntries = model.getIndexEntryCount();
-        int numMeshes = 0;
-        for (int i = 0; i < numEntries; i ++) {
-            FileA3D.IndexEntry entry = model.getIndexEntry(i);
-            if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                numMeshes ++;
-            }
-        }
-
-        if (numMeshes > 0) {
-            mMeshes = new ScriptField_MeshInfo(mRS, numMeshes);
-
-            for (int i = 0; i < numEntries; i ++) {
-                FileA3D.IndexEntry entry = model.getIndexEntry(i);
-                if (entry != null && entry.getEntryType() == FileA3D.EntryType.MESH) {
-                    Mesh mesh = entry.getMesh();
-                    mMeshes.set_mMesh(i, mesh, false);
-                    mMeshes.set_mNumIndexSets(i, mesh.getPrimitiveCount(), false);
-                }
-            }
-            mMeshes.copyAll();
-        } else {
-            throw new RSRuntimeException("No valid meshes in file");
-        }
-
-        mScript.bind_gMeshes(mMeshes);
-        mScript.invoke_updateMeshInfo();
-    }
-
-    private void initRS() {
-        mScript = new ScriptC_shaderstest(mRS, mRes, R.raw.shaderstest);
-
-        initPFS();
-        initPF();
-        initPV();
-
-        loadImage();
-
-        initBuffers(1, 1);
-
-        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
-        initMeshes(model);
-
-        mRS.bindRootScript(mScript);
-    }
-
-    private void initBuffers(int width, int height) {
-        Builder b;
-        b = new Builder(mRS, Element.RGBA_8888(mRS));
-        b.setX(width).setY(height);
-        mScreen = Allocation.createTyped(mRS, b.create(),
-                Allocation.USAGE_GRAPHICS_TEXTURE | Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gScreen(mScreen);
-
-        b = new Builder(mRS, Element.createPixel(mRS, DataType.UNSIGNED_16, DataKind.PIXEL_DEPTH));
-        b.setX(width).setY(height);
-        mScreenDepth = Allocation.createTyped(mRS, b.create(),
-                Allocation.USAGE_GRAPHICS_RENDER_TARGET);
-        mScript.set_gScreenDepth(mScreenDepth);
-    }
-}
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java
deleted file mode 100644
index e0a540f..0000000
--- a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/ShadersTestView.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.shaderstest;
-
-import android.content.Context;
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.SurfaceHolder;
-
-public class ShadersTestView extends RSSurfaceView {
-
-    private RenderScriptGL mRS;
-    private ShadersTestRS mRender;
-
-    private ScaleGestureDetector mScaleDetector;
-
-    private static final int INVALID_POINTER_ID = -1;
-    private int mActivePointerId = INVALID_POINTER_ID;
-
-    public ShadersTestView(Context context) {
-        super(context);
-        ensureRenderScript();
-        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
-    }
-
-    private void ensureRenderScript() {
-        if (mRS == null) {
-            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
-            sc.setDepth(16, 24);
-            mRS = createRenderScriptGL(sc);
-            mRender = new ShadersTestRS();
-            mRender.init(mRS, getResources());
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ensureRenderScript();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        mRender.surfaceChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mRender = null;
-        if (mRS != null) {
-            mRS = null;
-            destroyRenderScriptGL();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mScaleDetector.onTouchEvent(ev);
-
-        boolean ret = false;
-        float x = ev.getX();
-        float y = ev.getY();
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN: {
-            mRender.onActionDown(x, y);
-            mActivePointerId = ev.getPointerId(0);
-            ret = true;
-            break;
-        }
-        case MotionEvent.ACTION_MOVE: {
-            if (!mScaleDetector.isInProgress()) {
-                mRender.onActionMove(x, y);
-            }
-            mRender.onActionDown(x, y);
-            ret = true;
-            break;
-        }
-
-        case MotionEvent.ACTION_UP: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_CANCEL: {
-            mActivePointerId = INVALID_POINTER_ID;
-            break;
-        }
-
-        case MotionEvent.ACTION_POINTER_UP: {
-            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
-                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-            final int pointerId = ev.getPointerId(pointerIndex);
-            if (pointerId == mActivePointerId) {
-                // This was our active pointer going up. Choose a new
-                // active pointer and adjust accordingly.
-                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-                x = ev.getX(newPointerIndex);
-                y = ev.getY(newPointerIndex);
-                mRender.onActionDown(x, y);
-                mActivePointerId = ev.getPointerId(newPointerIndex);
-            }
-            break;
-        }
-        }
-
-        return ret;
-    }
-
-    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-        @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            mRender.onActionScale(detector.getScaleFactor());
-            return true;
-        }
-    }
-}
-
-
diff --git a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs b/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
deleted file mode 100644
index 735f6b9..0000000
--- a/tests/RenderScriptTests/ShadersTest/src/com/android/shaderstest/shaderstest.rs
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma version(1)
-
-#pragma rs java_package_name(com.android.shaderstest)
-
-#include "rs_graphics.rsh"
-
-rs_program_vertex gPVBackground;
-rs_program_fragment gPFBackground;
-
-typedef struct VignetteConstants_s {
-    float size;
-    float feather;
-    float width;
-    float height;
-} VignetteConstants;
-VignetteConstants *gFSVignetteConstants;
-rs_program_fragment gPFVignette;
-
-rs_allocation gTMesh;
-
-rs_sampler gLinear;
-rs_sampler gNearest;
-
-rs_program_store gPFSBackground;
-
-rs_allocation gScreenDepth;
-rs_allocation gScreen;
-
-typedef struct MeshInfo {
-    rs_mesh mMesh;
-    int mNumIndexSets;
-    float3 bBoxMin;
-    float3 bBoxMax;
-} MeshInfo_t;
-MeshInfo_t *gMeshes;
-
-static float3 gLookAt;
-
-static float gRotateX;
-static float gRotateY;
-static float gZoom;
-
-static float gLastX;
-static float gLastY;
-
-static float3 toFloat3(float x, float y, float z) {
-    float3 f;
-    f.x = x;
-    f.y = y;
-    f.z = z;
-    return f;
-}
-
-void onActionDown(float x, float y) {
-    gLastX = x;
-    gLastY = y;
-}
-
-void onActionScale(float scale) {
-
-    gZoom *= 1.0f / scale;
-    gZoom = max(0.1f, min(gZoom, 500.0f));
-}
-
-void onActionMove(float x, float y) {
-    float dx = gLastX - x;
-    float dy = gLastY - y;
-
-    if (fabs(dy) <= 2.0f) {
-        dy = 0.0f;
-    }
-    if (fabs(dx) <= 2.0f) {
-        dx = 0.0f;
-    }
-
-    gRotateY -= dx;
-    if (gRotateY > 360) {
-        gRotateY -= 360;
-    }
-    if (gRotateY < 0) {
-        gRotateY += 360;
-    }
-
-    gRotateX -= dy;
-    gRotateX = min(gRotateX, 80.0f);
-    gRotateX = max(gRotateX, -80.0f);
-
-    gLastX = x;
-    gLastY = y;
-}
-
-void init() {
-    gRotateX = 0.0f;
-    gRotateY = 0.0f;
-    gZoom = 50.0f;
-    gLookAt = 0.0f;
-}
-
-void updateMeshInfo() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    float minX, minY, minZ, maxX, maxY, maxZ;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgMeshComputeBoundingBox(info->mMesh,
-                                  &minX, &minY, &minZ,
-                                  &maxX, &maxY, &maxZ);
-        info->bBoxMin = toFloat3(minX, minY, minZ);
-        info->bBoxMax = toFloat3(maxX, maxY, maxZ);
-        gLookAt += (info->bBoxMin + info->bBoxMax)*0.5f;
-    }
-    gLookAt = gLookAt / (float)size;
-}
-
-static void renderAllMeshes() {
-    rs_allocation allMeshes = rsGetAllocation(gMeshes);
-    int size = rsAllocationGetDimX(allMeshes);
-    gLookAt = 0.0f;
-    for (int i = 0; i < size; i++) {
-        MeshInfo_t *info = (MeshInfo_t*)rsGetElementAt(allMeshes, i);
-        rsgDrawMesh(info->mMesh);
-    }
-}
-
-static void renderOffscreen() {
-    rsgBindProgramVertex(gPVBackground);
-    rs_matrix4x4 proj;
-    float aspect = (float) rsAllocationGetDimX(gScreen) / (float) rsAllocationGetDimY(gScreen);
-    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 1.0f, 1000.0f);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-
-    rsgBindProgramFragment(gPFBackground);
-    rsgBindTexture(gPFBackground, 0, gTMesh);
-
-    rs_matrix4x4 matrix;
-
-    rsMatrixLoadIdentity(&matrix);
-    rsMatrixTranslate(&matrix, gLookAt.x, gLookAt.y, gLookAt.z - gZoom);
-    rsMatrixRotate(&matrix, gRotateX, 1.0f, 0.0f, 0.0f);
-    rsMatrixRotate(&matrix, gRotateY, 0.0f, 1.0f, 0.0f);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-
-    renderAllMeshes();
-}
-
-static void drawOffscreenResult(int posX, int posY, float width, float height) {
-    // display the result d
-    rs_matrix4x4 proj, matrix;
-    rsMatrixLoadOrtho(&proj, 0, width, height, 0, -500, 500);
-    rsgProgramVertexLoadProjectionMatrix(&proj);
-    rsMatrixLoadIdentity(&matrix);
-    rsgProgramVertexLoadModelMatrix(&matrix);
-    float startX = posX, startY = posY;
-    rsgDrawQuadTexCoords(startX, startY, 0, 0, 1,
-                         startX, startY + height, 0, 0, 0,
-                         startX + width, startY + height, 0, 1, 0,
-                         startX + width, startY, 0, 1, 1);
-}
-
-int root(void) {
-    gFSVignetteConstants->size = 0.58f * 0.58f;
-    gFSVignetteConstants->feather = 0.2f;
-    gFSVignetteConstants->width = (float) rsAllocationGetDimX(gScreen);
-    gFSVignetteConstants->height = (float) rsAllocationGetDimY(gScreen);
-
-    rsgBindProgramStore(gPFSBackground);
-
-    // Render scene to fullscreenbuffer
-    rsgBindColorTarget(gScreen, 0);
-    rsgBindDepthTarget(gScreenDepth);
-    rsgClearDepth(1.0f);
-    rsgClearColor(1.0f, 1.0f, 1.0f, 0.0f);
-    renderOffscreen();
-
-    // Render on screen
-    rsgClearAllRenderTargets();
-    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
-    rsgClearDepth(1.0f);
-
-    rsgBindProgramFragment(gPFVignette);
-    rsgBindTexture(gPFVignette, 0, gScreen);
-    drawOffscreenResult(0, 0, rsgGetWidth(), rsgGetHeight());
-
-    return 0;
-}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index fecfdf9..3a30230 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -175,7 +175,7 @@
                         .setTopic(new Notification.Topic("hello", "Hello"))
                         .build();
 
-                mNM.notify(999, n);
+                mNM.notify(70, n);
             }
         },
 
@@ -194,7 +194,7 @@
                         .setStyle(picture)
                         .build();
 
-                mNM.notify(9999, n);
+                mNM.notify(71, n);
             }
         },
         new Test("with topic Bananas") {
@@ -211,10 +211,28 @@
                         .setTopic(new Notification.Topic("bananas", "Bananas"))
                         .build();
 
-                mNM.notify(999, n);
+                mNM.notify(72, n);
             }
         },
 
+            new Test("with delete intent") {
+                public void run() {
+                    Notification.BigTextStyle bigText = new Notification.BigTextStyle();
+                    bigText.bigText("bananas are great\nso tasty\nyum\nyum\nyum\n");
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon1)
+                            .setStyle(bigText)
+                            .setWhen(mActivityCreateTime)
+                            .setContentTitle("bananananana")
+                            .setContentText("This is a banana!!!")
+                            .setTopic(new Notification.Topic("bananas", "Bananas"))
+                            .setDeleteIntent(makeIntent2())
+                            .build();
+
+                    mNM.notify(73, n);
+                }
+            },
+
         new Test("Whens") {
             public void run()
             {
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 3c2659f..b78fd49 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
 
         try {
             mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null,
-                    Configuration.EMPTY, false);
+                    Configuration.EMPTY, false, false);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index e4738f5..5ad3379 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1095,13 +1095,6 @@
     analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
                   &paletteEntries, &hasTransparency, &color_type, outRows);
 
-    // If the image is a 9-patch, we need to preserve it as a ARGB file to make
-    // sure the pixels will not be pre-dithered/clamped until we decide they are
-    if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
-            color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
-        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
-    }
-
     if (kIsDebug) {
         switch (color_type) {
         case PNG_COLOR_TYPE_PALETTE:
@@ -1180,18 +1173,11 @@
         }
 
         for (int i = 0; i < chunk_count; i++) {
-            unknowns[i].location = PNG_HAVE_PLTE;
+            unknowns[i].location = PNG_HAVE_IHDR;
         }
         png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS,
                                     chunk_names, chunk_count);
         png_set_unknown_chunks(write_ptr, write_info, unknowns, chunk_count);
-#if PNG_LIBPNG_VER < 10600
-        /* Deal with unknown chunk location bug in 1.5.x and earlier */
-        png_set_unknown_chunk_location(write_ptr, write_info, 0, PNG_HAVE_PLTE);
-        if (imageInfo.haveLayoutBounds) {
-            png_set_unknown_chunk_location(write_ptr, write_info, 1, PNG_HAVE_PLTE);
-        }
-#endif
     }
 
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 576f076..4d9ba6c 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2119,7 +2119,7 @@
                 indentStr);
     }
 
-    return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+    return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR;
 }
 
 static status_t writeResourceLoadedCallback(
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 0f83980..a4f4ba9 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -27,6 +27,8 @@
 sources := \
 	compile/IdAssigner.cpp \
 	compile/Png.cpp \
+	compile/PseudolocaleGenerator.cpp \
+	compile/Pseudolocalizer.cpp \
 	compile/XmlIdCollector.cpp \
 	flatten/Archive.cpp \
 	flatten/TableFlattener.cpp \
@@ -66,6 +68,8 @@
 
 testSources := \
 	compile/IdAssigner_test.cpp \
+	compile/PseudolocaleGenerator_test.cpp \
+	compile/Pseudolocalizer_test.cpp \
 	compile/XmlIdCollector_test.cpp \
 	flatten/FileExportWriter_test.cpp \
 	flatten/TableFlattener_test.cpp \
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 64353de..13f8b3b 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -30,6 +30,11 @@
 
 static const char* kWildcardName = "any";
 
+const ConfigDescription& ConfigDescription::defaultConfig() {
+    static ConfigDescription config = {};
+    return config;
+}
+
 static bool parseMcc(const char* name, ResTable_config* out) {
     if (strcmp(name, kWildcardName) == 0) {
         if (out) out->mcc = 0;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 4af089d..5749816 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -29,6 +29,11 @@
  * initialization and comparison methods.
  */
 struct ConfigDescription : public android::ResTable_config {
+    /**
+     * Returns an immutable default config.
+     */
+    static const ConfigDescription& defaultConfig();
+
     /*
      * Parse a string of the form 'fr-sw600dp-land' and fill in the
      * given ResTable_config with resulting configuration parameters.
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index d864f66..b4e75f9 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -52,13 +52,17 @@
     void visit(Style* style) override {
         std::cout << "(style)";
         if (style->parent) {
+            const Reference& parentRef = style->parent.value();
             std::cout << " parent=";
-            if (style->parent.value().name) {
-                std::cout << style->parent.value().name.value() << " ";
+            if (parentRef.name) {
+                if (parentRef.privateReference) {
+                    std::cout << "*";
+                }
+                std::cout << parentRef.name.value() << " ";
             }
 
-            if (style->parent.value().id) {
-                std::cout << style->parent.value().id.value();
+            if (parentRef.id) {
+                std::cout << parentRef.id.value();
             }
         }
 
@@ -206,4 +210,19 @@
     std::cout << "}" << std::endl;
 }
 
+void Debug::dumpHex(const void* data, size_t len) {
+    const uint8_t* d = (const uint8_t*) data;
+    for (size_t i = 0; i < len; i++) {
+        std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t) d[i] << " ";
+        if (i % 8 == 7) {
+            std::cerr << "\n";
+        }
+    }
+
+    if (len - 1 % 8 != 7) {
+        std::cerr << std::endl;
+    }
+}
+
+
 } // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 5b0d7d6..ba05be9 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -20,12 +20,16 @@
 #include "Resource.h"
 #include "ResourceTable.h"
 
+// Include for printf-like debugging.
+#include <iostream>
+
 namespace aapt {
 
 struct Debug {
     static void printTable(ResourceTable* table);
     static void printStyleGraph(ResourceTable* table,
                                 const ResourceName& targetStyle);
+    static void dumpHex(const void* data, size_t len);
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index 9435396..666e8a8e 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -81,6 +81,8 @@
 }
 
 void Flags::usage(const StringPiece& command, std::ostream* out) {
+    constexpr size_t kWidth = 50;
+
     *out << command << " [options]";
     for (const Flag& flag : mFlags) {
         if (flag.required) {
@@ -100,11 +102,11 @@
         // the first line) followed by the description line. This will make sure that multiline
         // descriptions are still right justified and aligned.
         for (StringPiece line : util::tokenize<char>(flag.description, '\n')) {
-            *out << " " << std::setw(30) << std::left << argLine << line << "\n";
+            *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
             argLine = " ";
         }
     }
-    *out << " " << std::setw(30) << std::left << "-h" << "Displays this help menu\n";
+    *out << " " << std::setw(kWidth) << std::left << "-h" << "Displays this help menu\n";
     out->flush();
 }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index d4c536f..b37d366 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -19,9 +19,12 @@
 #include "ResourceUtils.h"
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
+#include "util/Comparators.h"
+#include "util/ImmutableMap.h"
 #include "util/Util.h"
 #include "xml/XmlPullParser.h"
 
+#include <functional>
 #include <sstream>
 
 namespace aapt {
@@ -35,6 +38,111 @@
     return ns.empty() && (name == u"skip" || name == u"eat-comment");
 }
 
+static uint32_t parseFormatType(const StringPiece16& piece) {
+    if (piece == u"reference")      return android::ResTable_map::TYPE_REFERENCE;
+    else if (piece == u"string")    return android::ResTable_map::TYPE_STRING;
+    else if (piece == u"integer")   return android::ResTable_map::TYPE_INTEGER;
+    else if (piece == u"boolean")   return android::ResTable_map::TYPE_BOOLEAN;
+    else if (piece == u"color")     return android::ResTable_map::TYPE_COLOR;
+    else if (piece == u"float")     return android::ResTable_map::TYPE_FLOAT;
+    else if (piece == u"dimension") return android::ResTable_map::TYPE_DIMENSION;
+    else if (piece == u"fraction")  return android::ResTable_map::TYPE_FRACTION;
+    else if (piece == u"enum")      return android::ResTable_map::TYPE_ENUM;
+    else if (piece == u"flags")     return android::ResTable_map::TYPE_FLAGS;
+    return 0;
+}
+
+static uint32_t parseFormatAttribute(const StringPiece16& str) {
+    uint32_t mask = 0;
+    for (StringPiece16 part : util::tokenize(str, u'|')) {
+        StringPiece16 trimmedPart = util::trimWhitespace(part);
+        uint32_t type = parseFormatType(trimmedPart);
+        if (type == 0) {
+            return 0;
+        }
+        mask |= type;
+    }
+    return mask;
+}
+
+/**
+ * A parsed resource ready to be added to the ResourceTable.
+ */
+struct ParsedResource {
+    ResourceName name;
+    ConfigDescription config;
+    Source source;
+    ResourceId id;
+    Maybe<SymbolState> symbolState;
+    std::u16string comment;
+    std::unique_ptr<Value> value;
+    std::list<ParsedResource> childResources;
+};
+
+bool ResourceParser::shouldStripResource(const ResourceNameRef& name,
+                                         const Maybe<std::u16string>& product) const {
+    if (product) {
+        for (const std::u16string& productToMatch : mOptions.products) {
+            if (product.value() == productToMatch) {
+                // We specified a product, and it is in the list, so don't strip.
+                return false;
+            }
+        }
+    }
+
+    // Nothing matched, try 'default'. Default only matches if we didn't already use another
+    // product variant.
+    if (!product || product.value() == u"default") {
+        if (Maybe<ResourceTable::SearchResult> result = mTable->findResource(name)) {
+            const ResourceEntry* entry = result.value().entry;
+            auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), mConfig,
+                                         cmp::lessThanConfig);
+            if (iter != entry->values.end() && iter->config == mConfig && !iter->value->isWeak()) {
+                // We have a value for this config already, and it is not weak,
+                // so filter out this default.
+                return true;
+            }
+        }
+        return false;
+    }
+    return true;
+}
+
+// Recursively adds resources to the ResourceTable.
+static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
+    if (res->symbolState) {
+        Symbol symbol;
+        symbol.state = res->symbolState.value();
+        symbol.source = res->source;
+        symbol.comment = res->comment;
+        if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
+            return false;
+        }
+    }
+
+    if (res->value) {
+        // Attach the comment, source and config to the value.
+        res->value->setComment(std::move(res->comment));
+        res->value->setSource(std::move(res->source));
+
+        if (!table->addResource(res->name, res->id, res->config, std::move(res->value), diag)) {
+            return false;
+        }
+    }
+
+    bool error = false;
+    for (ParsedResource& child : res->childResources) {
+        error |= !addResourcesToTable(table, diag, &child);
+    }
+    return !error;
+}
+
+// Convenient aliases for more readable function calls.
+enum {
+    kAllowRawString = true,
+    kNoRawString = false
+};
+
 ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
                                const ConfigDescription& config,
                                const ResourceParserOptions& options) :
@@ -146,69 +254,6 @@
     return !error;
 }
 
-static bool shouldStripResource(const xml::XmlPullParser* parser,
-                                const Maybe<std::u16string> productToMatch) {
-    assert(parser->getEvent() == xml::XmlPullParser::Event::kStartElement);
-
-    if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
-        if (!productToMatch) {
-            if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") {
-                // We didn't specify a product and this is not a default product, so skip.
-                return true;
-            }
-        } else {
-            if (productToMatch && maybeProduct.value() != productToMatch.value()) {
-                // We specified a product, but they don't match.
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-/**
- * A parsed resource ready to be added to the ResourceTable.
- */
-struct ParsedResource {
-    ResourceName name;
-    Source source;
-    ResourceId id;
-    Maybe<SymbolState> symbolState;
-    std::u16string comment;
-    std::unique_ptr<Value> value;
-    std::list<ParsedResource> childResources;
-};
-
-// Recursively adds resources to the ResourceTable.
-static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
-                                IDiagnostics* diag, ParsedResource* res) {
-    if (res->symbolState) {
-        Symbol symbol;
-        symbol.state = res->symbolState.value();
-        symbol.source = res->source;
-        symbol.comment = res->comment;
-        if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
-            return false;
-        }
-    }
-
-    if (res->value) {
-        // Attach the comment, source and config to the value.
-        res->value->setComment(std::move(res->comment));
-        res->value->setSource(std::move(res->source));
-
-        if (!table->addResource(res->name, res->id, config, std::move(res->value), diag)) {
-            return false;
-        }
-    }
-
-    bool error = false;
-    for (ParsedResource& child : res->childResources) {
-        error |= !addResourcesToTable(table, config, diag, &child);
-    }
-    return !error;
-}
-
 bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
     std::set<ResourceName> strippedResources;
 
@@ -244,118 +289,29 @@
             continue;
         }
 
-        if (elementName == u"item") {
-            // Items simply have their type encoded in the type attribute.
-            if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) {
-                elementName = maybeType.value().toString();
-            } else {
-                mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
-                             << "<item> must have a 'type' attribute");
-                error = true;
-                continue;
-            }
-        }
-
         ParsedResource parsedResource;
+        parsedResource.config = mConfig;
         parsedResource.source = mSource.withLine(parser->getLineNumber());
         parsedResource.comment = std::move(comment);
 
-        if (Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name")) {
-            parsedResource.name.entry = maybeName.value().toString();
+        // Extract the product name if it exists.
+        Maybe<std::u16string> product;
+        if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) {
+            product = maybeProduct.value().toString();
+        }
 
-        } else if (elementName != u"public-group") {
-            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
-                         << "<" << elementName << "> tag must have a 'name' attribute");
+        // Parse the resource regardless of product.
+        if (!parseResource(parser, &parsedResource)) {
             error = true;
             continue;
         }
 
-        // Check if we should skip this product.
-        const bool stripResource = shouldStripResource(parser, mOptions.product);
-
-        bool result = true;
-        if (elementName == u"id") {
-            parsedResource.name.type = ResourceType::kId;
-            parsedResource.value = util::make_unique<Id>();
-        } else if (elementName == u"string") {
-            parsedResource.name.type = ResourceType::kString;
-            result = parseString(parser, &parsedResource);
-        } else if (elementName == u"color") {
-            parsedResource.name.type = ResourceType::kColor;
-            result = parseColor(parser, &parsedResource);
-        } else if (elementName == u"drawable") {
-            parsedResource.name.type = ResourceType::kDrawable;
-            result = parseColor(parser, &parsedResource);
-        } else if (elementName == u"bool") {
-            parsedResource.name.type = ResourceType::kBool;
-            result = parsePrimitive(parser, &parsedResource);
-        } else if (elementName == u"integer") {
-            parsedResource.name.type = ResourceType::kInteger;
-            result = parsePrimitive(parser, &parsedResource);
-        } else if (elementName == u"dimen") {
-            parsedResource.name.type = ResourceType::kDimen;
-            result = parsePrimitive(parser, &parsedResource);
-        } else if (elementName == u"fraction") {
-            parsedResource.name.type = ResourceType::kFraction;
-            result = parsePrimitive(parser, &parsedResource);
-        } else if (elementName == u"style") {
-            parsedResource.name.type = ResourceType::kStyle;
-            result = parseStyle(parser, &parsedResource);
-        } else if (elementName == u"plurals") {
-            parsedResource.name.type = ResourceType::kPlurals;
-            result = parsePlural(parser, &parsedResource);
-        } else if (elementName == u"array") {
-            parsedResource.name.type = ResourceType::kArray;
-            result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_ANY);
-        } else if (elementName == u"string-array") {
-            parsedResource.name.type = ResourceType::kArray;
-            result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_STRING);
-        } else if (elementName == u"integer-array") {
-            parsedResource.name.type = ResourceType::kArray;
-            result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_INTEGER);
-        } else if (elementName == u"declare-styleable") {
-            parsedResource.name.type = ResourceType::kStyleable;
-            result = parseDeclareStyleable(parser, &parsedResource);
-        } else if (elementName == u"attr") {
-            parsedResource.name.type = ResourceType::kAttr;
-            result = parseAttr(parser, &parsedResource);
-        } else if (elementName == u"public") {
-            result = parsePublic(parser, &parsedResource);
-        } else if (elementName == u"java-symbol" || elementName == u"symbol") {
-            result = parseSymbol(parser, &parsedResource);
-        } else if (elementName == u"public-group") {
-            result = parsePublicGroup(parser, &parsedResource);
-        } else if (elementName == u"add-resource") {
-            result = parseAddResource(parser, &parsedResource);
-        } else {
-            // Try parsing the elementName (or type) as a resource. These shall only be
-            // resources like 'layout' or 'xml' and they can only be references.
-            if (const ResourceType* type = parseResourceType(elementName)) {
-                parsedResource.name.type = *type;
-                parsedResource.value = parseXml(parser, android::ResTable_map::TYPE_REFERENCE,
-                                                false);
-                if (!parsedResource.value) {
-                    mDiag->error(DiagMessage(parsedResource.source) << "invalid value for type '"
-                                 << *type << "'. Expected a reference");
-                    result = false;
-                }
-            } else {
-                mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
-                            << "unknown resource type '" << elementName << "'");
-            }
-        }
-
-        if (result) {
-            // We successfully parsed the resource.
-
-            if (stripResource) {
-                // Record that we stripped out this resource name.
-                // We will check that at least one variant of this resource was included.
-                strippedResources.insert(parsedResource.name);
-            } else {
-                error |= !addResourcesToTable(mTable, mConfig, mDiag, &parsedResource);
-            }
-        } else {
+        // We successfully parsed the resource. Check if we should include it or strip it.
+        if (shouldStripResource(parsedResource.name, product)) {
+            // Record that we stripped out this resource name.
+            // We will check that at least one variant of this resource was included.
+            strippedResources.insert(parsedResource.name);
+        } else if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
             error = true;
         }
     }
@@ -373,10 +329,173 @@
     return !error;
 }
 
-enum {
-    kAllowRawString = true,
-    kNoRawString = false
-};
+
+bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
+    struct ItemTypeFormat {
+        ResourceType type;
+        uint32_t format;
+    };
+
+    using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*, ParsedResource*)>;
+
+    static const auto elToItemMap = ImmutableMap<std::u16string, ItemTypeFormat>::createPreSorted({
+            { u"bool",      { ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN } },
+            { u"color",     { ResourceType::kColor, android::ResTable_map::TYPE_COLOR } },
+            { u"dimen",     { ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT
+                                                    | android::ResTable_map::TYPE_FRACTION
+                                                    | android::ResTable_map::TYPE_DIMENSION } },
+            { u"drawable",  { ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR } },
+            { u"fraction",  { ResourceType::kFraction, android::ResTable_map::TYPE_FLOAT
+                                                       | android::ResTable_map::TYPE_FRACTION
+                                                       | android::ResTable_map::TYPE_DIMENSION } },
+            { u"integer",   { ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER } },
+            { u"string",    { ResourceType::kString, android::ResTable_map::TYPE_STRING } },
+    });
+
+    static const auto elToBagMap = ImmutableMap<std::u16string, BagParseFunc>::createPreSorted({
+            { u"add-resource",      std::mem_fn(&ResourceParser::parseAddResource) },
+            { u"array",             std::mem_fn(&ResourceParser::parseArray) },
+            { u"attr",              std::mem_fn(&ResourceParser::parseAttr) },
+            { u"declare-styleable", std::mem_fn(&ResourceParser::parseDeclareStyleable) },
+            { u"integer-array",     std::mem_fn(&ResourceParser::parseIntegerArray) },
+            { u"java-symbol",       std::mem_fn(&ResourceParser::parseSymbol) },
+            { u"plurals",           std::mem_fn(&ResourceParser::parsePlural) },
+            { u"public",            std::mem_fn(&ResourceParser::parsePublic) },
+            { u"public-group",      std::mem_fn(&ResourceParser::parsePublicGroup) },
+            { u"string-array",      std::mem_fn(&ResourceParser::parseStringArray) },
+            { u"style",             std::mem_fn(&ResourceParser::parseStyle) },
+            { u"symbol",            std::mem_fn(&ResourceParser::parseSymbol) },
+    });
+
+    std::u16string resourceType = parser->getElementName();
+
+    // The value format accepted for this resource.
+    uint32_t resourceFormat = 0u;
+
+    if (resourceType == u"item") {
+        // Items have their type encoded in the type attribute.
+        if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) {
+            resourceType = maybeType.value().toString();
+        } else {
+            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+                         << "<item> must have a 'type' attribute");
+            return false;
+        }
+
+        if (Maybe<StringPiece16> maybeFormat = xml::findNonEmptyAttribute(parser, u"format")) {
+            // An explicit format for this resource was specified. The resource will retain
+            // its type in its name, but the accepted value for this type is overridden.
+            resourceFormat = parseFormatType(maybeFormat.value());
+            if (!resourceFormat) {
+                mDiag->error(DiagMessage(outResource->source)
+                             << "'" << maybeFormat.value() << "' is an invalid format");
+                return false;
+            }
+        }
+    }
+
+    // Get the name of the resource. This will be checked later, because not all
+    // XML elements require a name.
+    Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name");
+
+    if (resourceType == u"id") {
+        if (!maybeName) {
+            mDiag->error(DiagMessage(outResource->source)
+                         << "<" << parser->getElementName() << "> missing 'name' attribute");
+            return false;
+        }
+
+        outResource->name.type = ResourceType::kId;
+        outResource->name.entry = maybeName.value().toString();
+        outResource->value = util::make_unique<Id>();
+        return true;
+    }
+
+    const auto itemIter = elToItemMap.find(resourceType);
+    if (itemIter != elToItemMap.end()) {
+        // This is an item, record its type and format and start parsing.
+
+        if (!maybeName) {
+            mDiag->error(DiagMessage(outResource->source)
+                         << "<" << parser->getElementName() << "> missing 'name' attribute");
+            return false;
+        }
+
+        outResource->name.type = itemIter->second.type;
+        outResource->name.entry = maybeName.value().toString();
+
+        // Only use the implicit format for this type if it wasn't overridden.
+        if (!resourceFormat) {
+            resourceFormat = itemIter->second.format;
+        }
+
+        if (!parseItem(parser, outResource, resourceFormat)) {
+            return false;
+        }
+        return true;
+    }
+
+    // This might be a bag or something.
+    const auto bagIter = elToBagMap.find(resourceType);
+    if (bagIter != elToBagMap.end()) {
+        // Ensure we have a name (unless this is a <public-group>).
+        if (resourceType != u"public-group") {
+            if (!maybeName) {
+                mDiag->error(DiagMessage(outResource->source)
+                             << "<" << parser->getElementName() << "> missing 'name' attribute");
+                return false;
+            }
+
+            outResource->name.entry = maybeName.value().toString();
+        }
+
+        // Call the associated parse method. The type will be filled in by the
+        // parse func.
+        if (!bagIter->second(this, parser, outResource)) {
+            return false;
+        }
+        return true;
+    }
+
+    // Try parsing the elementName (or type) as a resource. These shall only be
+    // resources like 'layout' or 'xml' and they can only be references.
+    const ResourceType* parsedType = parseResourceType(resourceType);
+    if (parsedType) {
+        if (!maybeName) {
+            mDiag->error(DiagMessage(outResource->source)
+                         << "<" << parser->getElementName() << "> missing 'name' attribute");
+            return false;
+        }
+
+        outResource->name.type = *parsedType;
+        outResource->name.entry = maybeName.value().toString();
+        outResource->value = parseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
+        if (!outResource->value) {
+            mDiag->error(DiagMessage(outResource->source)
+                         << "invalid value for type '" << *parsedType << "'. Expected a reference");
+            return false;
+        }
+        return true;
+    }
+
+    mDiag->warn(DiagMessage(outResource->source)
+                << "unknown resource type '" << parser->getElementName() << "'");
+    return false;
+}
+
+bool ResourceParser::parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
+                               const uint32_t format) {
+    if (format == android::ResTable_map::TYPE_STRING) {
+        return parseString(parser, outResource);
+    }
+
+    outResource->value = parseXml(parser, format, kNoRawString);
+    if (!outResource->value) {
+        mDiag->error(DiagMessage(outResource->source) << "invalid " << outResource->name.type);
+        return false;
+    }
+    return true;
+}
 
 /**
  * Reads the entire XML subtree and attempts to parse it as some Item,
@@ -431,17 +550,15 @@
         return util::make_unique<RawString>(
                 mTable->stringPool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
     }
-
     return {};
 }
 
 bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-
     bool formatted = true;
     if (Maybe<StringPiece16> formattedAttr = xml::findAttribute(parser, u"formatted")) {
         if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) {
-            mDiag->error(DiagMessage(source) << "invalid value for 'formatted'. Must be a boolean");
+            mDiag->error(DiagMessage(outResource->source)
+                         << "invalid value for 'formatted'. Must be a boolean");
             return false;
         }
     }
@@ -449,7 +566,7 @@
     bool translateable = mOptions.translatable;
     if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) {
         if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) {
-            mDiag->error(DiagMessage(source)
+            mDiag->error(DiagMessage(outResource->source)
                          << "invalid value for 'translatable'. Must be a boolean");
             return false;
         }
@@ -457,81 +574,44 @@
 
     outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
     if (!outResource->value) {
-        mDiag->error(DiagMessage(source) << "not a valid string");
+        mDiag->error(DiagMessage(outResource->source) << "not a valid string");
         return false;
     }
 
-    if (formatted && translateable) {
-        if (String* stringValue = valueCast<String>(outResource->value.get())) {
+    if (String* stringValue = valueCast<String>(outResource->value.get())) {
+        stringValue->setTranslateable(translateable);
+
+        if (formatted && translateable) {
             if (!util::verifyJavaStringFormat(*stringValue->value)) {
-                mDiag->error(DiagMessage(source)
-                             << "multiple substitutions specified in non-positional format; "
-                                "did you mean to add the formatted=\"false\" attribute?");
-                return false;
+                DiagMessage msg(outResource->source);
+                msg << "multiple substitutions specified in non-positional format; "
+                       "did you mean to add the formatted=\"false\" attribute?";
+                if (mOptions.errorOnPositionalArguments) {
+                    mDiag->error(msg);
+                    return false;
+                }
+
+                mDiag->warn(msg);
             }
         }
-    }
-    return true;
-}
 
-bool ResourceParser::parseColor(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-
-    outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
-    if (!outResource->value) {
-        mDiag->error(DiagMessage(source) << "invalid color");
-        return false;
-    }
-    return true;
-}
-
-bool ResourceParser::parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-
-    uint32_t typeMask = 0;
-    switch (outResource->name.type) {
-    case ResourceType::kInteger:
-        typeMask |= android::ResTable_map::TYPE_INTEGER;
-        break;
-
-    case ResourceType::kFraction:
-        // fallthrough
-    case ResourceType::kDimen:
-        typeMask |= android::ResTable_map::TYPE_DIMENSION
-                  | android::ResTable_map::TYPE_FLOAT
-                  | android::ResTable_map::TYPE_FRACTION;
-        break;
-
-    case ResourceType::kBool:
-        typeMask |= android::ResTable_map::TYPE_BOOLEAN;
-        break;
-
-    default:
-        assert(false);
-        break;
-    }
-
-    outResource->value = parseXml(parser, typeMask, kNoRawString);
-    if (!outResource->value) {
-        mDiag->error(DiagMessage(source) << "invalid " << outResource->name.type);
-        return false;
+    } else if (StyledString* stringValue = valueCast<StyledString>(outResource->value.get())) {
+        stringValue->setTranslateable(translateable);
     }
     return true;
 }
 
 bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-
     Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
     if (!maybeType) {
-        mDiag->error(DiagMessage(source) << "<public> must have a 'type' attribute");
+        mDiag->error(DiagMessage(outResource->source) << "<public> must have a 'type' attribute");
         return false;
     }
 
     const ResourceType* parsedType = parseResourceType(maybeType.value());
     if (!parsedType) {
-        mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
-                     << "' in <public>");
+        mDiag->error(DiagMessage(outResource->source)
+                     << "invalid resource type '" << maybeType.value() << "' in <public>");
         return false;
     }
 
@@ -543,8 +623,8 @@
                                                      maybeId.value().size(), &val);
         ResourceId resourceId(val.data);
         if (!result || !resourceId.isValid()) {
-            mDiag->error(DiagMessage(source) << "invalid resource ID '" << maybeId.value()
-                         << "' in <public>");
+            mDiag->error(DiagMessage(outResource->source)
+                         << "invalid resource ID '" << maybeId.value() << "' in <public>");
             return false;
         }
         outResource->id = resourceId;
@@ -560,24 +640,24 @@
 }
 
 bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-
     Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
     if (!maybeType) {
-        mDiag->error(DiagMessage(source) << "<public-group> must have a 'type' attribute");
+        mDiag->error(DiagMessage(outResource->source)
+                     << "<public-group> must have a 'type' attribute");
         return false;
     }
 
     const ResourceType* parsedType = parseResourceType(maybeType.value());
     if (!parsedType) {
-        mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
-                     << "' in <public-group>");
+        mDiag->error(DiagMessage(outResource->source)
+                     << "invalid resource type '" << maybeType.value() << "' in <public-group>");
         return false;
     }
 
     Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"first-id");
     if (!maybeId) {
-        mDiag->error(DiagMessage(source) << "<public-group> must have a 'first-id' attribute");
+        mDiag->error(DiagMessage(outResource->source)
+                     << "<public-group> must have a 'first-id' attribute");
         return false;
     }
 
@@ -586,8 +666,8 @@
                                                  maybeId.value().size(), &val);
     ResourceId nextId(val.data);
     if (!result || !nextId.isValid()) {
-        mDiag->error(DiagMessage(source) << "invalid resource ID '" << maybeId.value()
-                     << "' in <public-group>");
+        mDiag->error(DiagMessage(outResource->source)
+                     << "invalid resource ID '" << maybeId.value() << "' in <public-group>");
         return false;
     }
 
@@ -646,18 +726,17 @@
 }
 
 bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-
     Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
     if (!maybeType) {
-        mDiag->error(DiagMessage(source) << "<" << parser->getElementName() << "> must have a "
-                     "'type' attribute");
+        mDiag->error(DiagMessage(outResource->source)
+                     << "<" << parser->getElementName() << "> must have a 'type' attribute");
         return false;
     }
 
     const ResourceType* parsedType = parseResourceType(maybeType.value());
     if (!parsedType) {
-        mDiag->error(DiagMessage(source) << "invalid resource type '" << maybeType.value()
+        mDiag->error(DiagMessage(outResource->source)
+                     << "invalid resource type '" << maybeType.value()
                      << "' in <" << parser->getElementName() << ">");
         return false;
     }
@@ -682,40 +761,22 @@
     return false;
 }
 
-static uint32_t parseFormatType(const StringPiece16& piece) {
-    if (piece == u"reference")      return android::ResTable_map::TYPE_REFERENCE;
-    else if (piece == u"string")    return android::ResTable_map::TYPE_STRING;
-    else if (piece == u"integer")   return android::ResTable_map::TYPE_INTEGER;
-    else if (piece == u"boolean")   return android::ResTable_map::TYPE_BOOLEAN;
-    else if (piece == u"color")     return android::ResTable_map::TYPE_COLOR;
-    else if (piece == u"float")     return android::ResTable_map::TYPE_FLOAT;
-    else if (piece == u"dimension") return android::ResTable_map::TYPE_DIMENSION;
-    else if (piece == u"fraction")  return android::ResTable_map::TYPE_FRACTION;
-    else if (piece == u"enum")      return android::ResTable_map::TYPE_ENUM;
-    else if (piece == u"flags")     return android::ResTable_map::TYPE_FLAGS;
-    return 0;
-}
-
-static uint32_t parseFormatAttribute(const StringPiece16& str) {
-    uint32_t mask = 0;
-    for (StringPiece16 part : util::tokenize(str, u'|')) {
-        StringPiece16 trimmedPart = util::trimWhitespace(part);
-        uint32_t type = parseFormatType(trimmedPart);
-        if (type == 0) {
-            return 0;
-        }
-        mask |= type;
-    }
-    return mask;
-}
 
 bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    outResource->source = mSource.withLine(parser->getLineNumber());
     return parseAttrImpl(parser, outResource, false);
 }
 
 bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
                                    bool weak) {
+    outResource->name.type = ResourceType::kAttr;
+
+    // Attributes only end up in default configuration.
+    if (outResource->config != ConfigDescription::defaultConfig()) {
+        mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
+                    << outResource->config << "' for attribute " << outResource->name);
+        outResource->config = ConfigDescription::defaultConfig();
+    }
+
     uint32_t typeMask = 0;
 
     Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format");
@@ -887,8 +948,7 @@
     }
 
     return Attribute::Symbol{
-            Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())),
-            val.data };
+            Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
 }
 
 static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) {
@@ -949,7 +1009,8 @@
 }
 
 bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
+    outResource->name.type = ResourceType::kStyle;
+
     std::unique_ptr<Style> style = util::make_unique<Style>();
 
     Maybe<StringPiece16> maybeParent = xml::findAttribute(parser, u"parent");
@@ -959,7 +1020,7 @@
             std::string errStr;
             style->parent = ResourceUtils::parseStyleParentReference(maybeParent.value(), &errStr);
             if (!style->parent) {
-                mDiag->error(DiagMessage(source) << errStr);
+                mDiag->error(DiagMessage(outResource->source) << errStr);
                 return false;
             }
 
@@ -1007,9 +1068,22 @@
     return true;
 }
 
-bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource,
-                                uint32_t typeMask) {
-    const Source source = mSource.withLine(parser->getLineNumber());
+bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
+    return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_ANY);
+}
+
+bool ResourceParser::parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
+    return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_INTEGER);
+}
+
+bool ResourceParser::parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
+    return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_STRING);
+}
+
+bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+                                    const uint32_t typeMask) {
+    outResource->name.type = ResourceType::kArray;
+
     std::unique_ptr<Array> array = util::make_unique<Array>();
 
     bool error = false;
@@ -1049,7 +1123,8 @@
 }
 
 bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
+    outResource->name.type = ResourceType::kPlurals;
+
     std::unique_ptr<Plural> plural = util::make_unique<Plural>();
 
     bool error = false;
@@ -1122,13 +1197,23 @@
     return true;
 }
 
-bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource) {
-    const Source source = mSource.withLine(parser->getLineNumber());
-    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
+                                           ParsedResource* outResource) {
+    outResource->name.type = ResourceType::kStyleable;
 
     // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
     outResource->symbolState = SymbolState::kPublic;
 
+    // Declare-styleable only ends up in default config;
+    if (outResource->config != ConfigDescription::defaultConfig()) {
+        mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
+                            << outResource->config << "' for styleable "
+                            << outResource->name.entry);
+        outResource->config = ConfigDescription::defaultConfig();
+    }
+
+    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+
     std::u16string comment;
     bool error = false;
     const size_t depth = parser->getDepth();
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 04db577..51cbbe1 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -34,16 +34,21 @@
 
 struct ResourceParserOptions {
     /**
-     * Optional product name by which to filter resources.
+     * Optional product names by which to filter resources.
      * This is like a preprocessor definition in that we strip out resources
      * that don't match before we compile them.
      */
-    Maybe<std::u16string> product;
+    std::vector<std::u16string> products;
 
     /**
      * Whether the default setting for this parser is to allow translation.
      */
     bool translatable = true;
+
+    /**
+     * Whether positional arguments in formatted strings are treated as errors or warnings.
+     */
+    bool errorOnPositionalArguments = true;
 };
 
 /*
@@ -78,9 +83,11 @@
                                    const bool allowRawValue);
 
     bool parseResources(xml::XmlPullParser* parser);
+    bool parseResource(xml::XmlPullParser* parser, ParsedResource* outResource);
+
+    bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t format);
     bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
-    bool parseColor(xml::XmlPullParser* parser, ParsedResource* outResource);
-    bool parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource);
+
     bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
@@ -93,9 +100,15 @@
     bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
     bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource);
-    bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
+    bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource);
+    bool parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource);
+    bool parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource);
+    bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
     bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
 
+    bool shouldStripResource(const ResourceNameRef& name,
+                             const Maybe<std::u16string>& product) const;
+
     IDiagnostics* mDiag;
     ResourceTable* mTable;
     Source mSource;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 84f67c6..cf0fcd1 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -47,13 +47,26 @@
         mContext = test::ContextBuilder().build();
     }
 
+    ::testing::AssertionResult testParse(const StringPiece& str) {
+        return testParse(str, ConfigDescription{}, {});
+    }
+
+    ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
+        return testParse(str, config, {});
+    }
+
     ::testing::AssertionResult testParse(const StringPiece& str,
-                                         Maybe<std::u16string> product = {}) {
+                                         std::initializer_list<std::u16string> products) {
+        return testParse(str, {}, std::move(products));
+    }
+
+    ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
+                                         std::initializer_list<std::u16string> products) {
         std::stringstream input(kXmlPreamble);
         input << "<resources>\n" << str << "\n</resources>" << std::endl;
         ResourceParserOptions parserOptions;
-        parserOptions.product = product;
-        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
+        parserOptions.products = products;
+        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
                               parserOptions);
         xml::XmlPullParser xmlParser(input);
         if (parser.parse(&xmlParser)) {
@@ -138,6 +151,26 @@
     EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
 }
 
+// Old AAPT allowed attributes to be defined under different configurations, but ultimately
+// stored them with the default configuration. Check that we have the same behavior.
+TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
+    const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
+    std::string input = R"EOF(
+        <attr name="foo" />
+        <declare-styleable name="bar">
+          <attr name="baz" />
+        </declare-styleable>)EOF";
+    ASSERT_TRUE(testParse(input, watchConfig));
+
+    EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/foo", watchConfig));
+    EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/baz", watchConfig));
+    EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, u"@styleable/bar", watchConfig));
+
+    EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/foo"));
+    EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/baz"));
+    EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, u"@styleable/bar"));
+}
+
 TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
     std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
     ASSERT_TRUE(testParse(input));
@@ -215,7 +248,7 @@
     ASSERT_TRUE(testParse(input));
 
     Attribute* flagAttr = test::getValue<Attribute>(&mTable, u"@attr/foo");
-    ASSERT_NE(flagAttr, nullptr);
+    ASSERT_NE(nullptr, flagAttr);
     EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
     ASSERT_EQ(flagAttr->symbols.size(), 3u);
 
@@ -233,7 +266,7 @@
 
     std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr,
                                                                                    u"baz|bat");
-    ASSERT_NE(flagValue, nullptr);
+    ASSERT_NE(nullptr, flagValue);
     EXPECT_EQ(flagValue->value.data, 1u | 2u);
 }
 
@@ -255,7 +288,7 @@
     ASSERT_TRUE(testParse(input));
 
     Style* style = test::getValue<Style>(&mTable, u"@style/foo");
-    ASSERT_NE(style, nullptr);
+    ASSERT_NE(nullptr, style);
     AAPT_ASSERT_TRUE(style->parent);
     AAPT_ASSERT_TRUE(style->parent.value().name);
     EXPECT_EQ(test::parseNameOrDie(u"@style/fu"), style->parent.value().name.value());
@@ -276,7 +309,7 @@
     ASSERT_TRUE(testParse(input));
 
     Style* style = test::getValue<Style>(&mTable, u"@style/foo");
-    ASSERT_NE(style, nullptr);
+    ASSERT_NE(nullptr, style);
     AAPT_ASSERT_TRUE(style->parent);
     AAPT_ASSERT_TRUE(style->parent.value().name);
     EXPECT_EQ(test::parseNameOrDie(u"@com.app:style/Theme"), style->parent.value().name.value());
@@ -288,7 +321,7 @@
     ASSERT_TRUE(testParse(input));
 
     Style* style = test::getValue<Style>(&mTable, u"@style/foo");
-    ASSERT_NE(style, nullptr);
+    ASSERT_NE(nullptr, style);
     AAPT_ASSERT_TRUE(style->parent);
     AAPT_ASSERT_TRUE(style->parent.value().name);
     EXPECT_EQ(test::parseNameOrDie(u"@android:style/Theme"), style->parent.value().name.value());
@@ -302,7 +335,7 @@
     ASSERT_TRUE(testParse(input));
 
     Style* style = test::getValue<Style>(&mTable, u"@style/foo");
-    ASSERT_NE(style, nullptr);
+    ASSERT_NE(nullptr, style);
     ASSERT_EQ(1u, style->entries.size());
     EXPECT_EQ(test::parseNameOrDie(u"@android:attr/bar"), style->entries[0].key.name.value());
 }
@@ -312,7 +345,7 @@
     ASSERT_TRUE(testParse(input));
 
     Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
-    ASSERT_NE(style, nullptr);
+    ASSERT_NE(nullptr, style);
     AAPT_ASSERT_TRUE(style->parent);
     AAPT_ASSERT_TRUE(style->parent.value().name);
     EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie(u"@style/foo"));
@@ -324,11 +357,21 @@
     ASSERT_TRUE(testParse(input));
 
     Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar");
-    ASSERT_NE(style, nullptr);
+    ASSERT_NE(nullptr, style);
     AAPT_EXPECT_FALSE(style->parent);
     EXPECT_FALSE(style->parentInferred);
 }
 
+TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
+    std::string input = R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
+    ASSERT_TRUE(testParse(input));
+
+    Style* style = test::getValue<Style>(&mTable, u"@style/foo");
+    ASSERT_NE(nullptr, style);
+    AAPT_ASSERT_TRUE(style->parent);
+    EXPECT_TRUE(style->parent.value().privateReference);
+}
+
 TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
     std::string input = "<string name=\"foo\">@+id/bar</string>";
     ASSERT_TRUE(testParse(input));
@@ -504,11 +547,15 @@
 }
 
 TEST_F(ResourceParserTest, FilterProductsThatDontMatch) {
-    std::string input = "<string name=\"foo\" product=\"phone\">hi</string>\n"
-                        "<string name=\"foo\" product=\"no-sdcard\">ho</string>\n"
-                        "<string name=\"bar\" product=\"\">wee</string>\n"
-                        "<string name=\"baz\">woo</string>\n";
-    ASSERT_TRUE(testParse(input, std::u16string(u"no-sdcard")));
+    std::string input = R"EOF(
+        <string name="foo" product="phone">hi</string>
+        <string name="foo" product="no-sdcard">ho</string>
+        <string name="bar" product="">wee</string>
+        <string name="baz">woo</string>
+        <string name="bit" product="phablet">hoot</string>
+        <string name="bot" product="default">yes</string>
+    )EOF";
+    ASSERT_TRUE(testParse(input, { std::u16string(u"no-sdcard"), std::u16string(u"phablet") }));
 
     String* fooStr = test::getValue<String>(&mTable, u"@string/foo");
     ASSERT_NE(nullptr, fooStr);
@@ -516,11 +563,25 @@
 
     EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bar"));
     EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/baz"));
+    EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bit"));
+    EXPECT_NE(nullptr, test::getValue<String>(&mTable, u"@string/bot"));
+}
+
+TEST_F(ResourceParserTest, FilterProductsThatBothMatchInOrder) {
+    std::string input = R"EOF(
+        <string name="foo" product="phone">phone</string>
+        <string name="foo" product="default">default</string>
+    )EOF";
+    ASSERT_TRUE(testParse(input, { std::u16string(u"phone") }));
+
+    String* foo = test::getValue<String>(&mTable, u"@string/foo");
+    ASSERT_NE(nullptr, foo);
+    EXPECT_EQ(std::u16string(u"phone"), *foo->value);
 }
 
 TEST_F(ResourceParserTest, FailWhenProductFilterStripsOutAllVersionsOfResource) {
     std::string input = "<string name=\"foo\" product=\"tablet\">hello</string>\n";
-    ASSERT_FALSE(testParse(input, std::u16string(u"phone")));
+    ASSERT_FALSE(testParse(input, { std::u16string(u"phone") }));
 }
 
 TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
@@ -575,4 +636,14 @@
     EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
 }
 
+TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
+    std::string input = R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF";
+    ASSERT_TRUE(testParse(input));
+
+    BinaryPrimitive* val = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo");
+    ASSERT_NE(nullptr, val);
+
+    EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 36c3e70..07f62af 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -176,10 +176,10 @@
 /*
  * Style parent's are a bit different. We accept the following formats:
  *
- * @[package:]style/<entry>
- * ?[package:]style/<entry>
- * <package>:[style/]<entry>
- * [package:style/]<entry>
+ * @[[*]package:][style/]<entry>
+ * ?[[*]package:]style/<entry>
+ * <[*]package>:[style/]<entry>
+ * [[*]package:style/]<entry>
  */
 Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError) {
     if (str.empty()) {
@@ -195,10 +195,11 @@
     if (name.data()[0] == u'@' || name.data()[0] == u'?') {
         hasLeadingIdentifiers = true;
         name = name.substr(1, name.size() - 1);
-        if (name.data()[0] == u'*') {
-            privateRef = true;
-            name = name.substr(1, name.size() - 1);
-        }
+    }
+
+    if (name.data()[0] == u'*') {
+        privateRef = true;
+        name = name.substr(1, name.size() - 1);
     }
 
     ResourceNameRef ref;
@@ -215,14 +216,6 @@
             *outError = err.str();
             return {};
         }
-    } else {
-        // No type was defined, this should not have a leading identifier.
-        if (hasLeadingIdentifiers) {
-            std::stringstream err;
-            err << "invalid parent reference '" << str << "'";
-            *outError = err.str();
-            return {};
-        }
     }
 
     if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
@@ -293,6 +286,12 @@
                                                     const StringPiece16& str) {
     android::Res_value flags = { };
     flags.dataType = android::Res_value::TYPE_INT_DEC;
+    flags.data = 0u;
+
+    if (util::trimWhitespace(str).empty()) {
+        // Empty string is a valid flag (0).
+        return util::make_unique<BinaryPrimitive>(flags);
+    }
 
     for (StringPiece16 part : util::tokenize(str, u'|')) {
         StringPiece16 trimmedPart = util::trimWhitespace(part);
@@ -385,12 +384,12 @@
 
 bool tryParseBool(const StringPiece16& str, bool* outValue) {
     StringPiece16 trimmedStr(util::trimWhitespace(str));
-    if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
+    if (trimmedStr == u"true" || trimmedStr == u"TRUE" || trimmedStr == u"True") {
         if (outValue) {
             *outValue = true;
         }
         return true;
-    } else if (trimmedStr == u"false" || trimmedStr == u"FALSE") {
+    } else if (trimmedStr == u"false" || trimmedStr == u"FALSE" || trimmedStr == u"False") {
         if (outValue) {
             *outValue = false;
         }
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 4bbfc32..c9f93e1 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -16,12 +16,34 @@
 
 #include "Resource.h"
 #include "ResourceUtils.h"
+#include "test/Builders.h"
 #include "test/Common.h"
 
 #include <gtest/gtest.h>
 
 namespace aapt {
 
+TEST(ResourceUtilsTest, ParseBool) {
+    bool val = false;
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"true", &val));
+    EXPECT_TRUE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"TRUE", &val));
+    EXPECT_TRUE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"True", &val));
+    EXPECT_TRUE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"false", &val));
+    EXPECT_FALSE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"FALSE", &val));
+    EXPECT_FALSE(val);
+
+    EXPECT_TRUE(ResourceUtils::tryParseBool(u"False", &val));
+    EXPECT_FALSE(val);
+}
+
 TEST(ResourceUtilsTest, ParseResourceName) {
     ResourceNameRef actual;
     bool actualPriv = false;
@@ -154,9 +176,30 @@
     AAPT_ASSERT_TRUE(ref);
     EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
 
+    ref = ResourceUtils::parseStyleParentReference(u"@android:foo", &errStr);
+    AAPT_ASSERT_TRUE(ref);
+    EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
     ref = ResourceUtils::parseStyleParentReference(u"foo", &errStr);
     AAPT_ASSERT_TRUE(ref);
     EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+
+    ref = ResourceUtils::parseStyleParentReference(u"*android:style/foo", &errStr);
+    AAPT_ASSERT_TRUE(ref);
+    EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+    EXPECT_TRUE(ref.value().privateReference);
+}
+
+TEST(ResourceUtilsTest, ParseEmptyFlag) {
+    std::unique_ptr<Attribute> attr = test::AttributeBuilder(false)
+            .setTypeMask(android::ResTable_map::TYPE_FLAGS)
+            .addItem(u"one", 0x01)
+            .addItem(u"two", 0x02)
+            .build();
+
+    std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), u"");
+    ASSERT_NE(nullptr, result);
+    EXPECT_EQ(0u, result->value.data);
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 04c375f..b93e6d8 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -36,10 +36,6 @@
     visitor->visit(static_cast<Derived*>(this));
 }
 
-bool Value::isWeak() const {
-    return false;
-}
-
 RawString::RawString(const StringPool::Ref& ref) : value(ref) {
 }
 
@@ -101,10 +97,6 @@
     }
 }
 
-bool Id::isWeak() const {
-    return true;
-}
-
 bool Id::flatten(android::Res_value* out) const {
     out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
     out->data = util::hostToDevice32(0);
@@ -119,7 +111,15 @@
     *out << "(id)";
 }
 
-String::String(const StringPool::Ref& ref) : value(ref) {
+String::String(const StringPool::Ref& ref) : value(ref), mTranslateable(true) {
+}
+
+void String::setTranslateable(bool val) {
+    mTranslateable = val;
+}
+
+bool String::isTranslateable() const {
+    return mTranslateable;
 }
 
 bool String::flatten(android::Res_value* outValue) const {
@@ -144,7 +144,15 @@
     *out << "(string) \"" << *value << "\"";
 }
 
-StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
+StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref), mTranslateable(true) {
+}
+
+void StyledString::setTranslateable(bool val) {
+    mTranslateable = val;
+}
+
+bool StyledString::isTranslateable() const {
+    return mTranslateable;
 }
 
 bool StyledString::flatten(android::Res_value* outValue) const {
@@ -238,13 +246,10 @@
 }
 
 Attribute::Attribute(bool w, uint32_t t) :
-        weak(w), typeMask(t),
+        typeMask(t),
         minInt(std::numeric_limits<int32_t>::min()),
         maxInt(std::numeric_limits<int32_t>::max()) {
-}
-
-bool Attribute::isWeak() const {
-    return weak;
+    mWeak = w;
 }
 
 Attribute* Attribute::clone(StringPool* /*newPool*/) const {
@@ -359,7 +364,7 @@
             << "]";
     }
 
-    if (weak) {
+    if (isWeak()) {
         *out << " [weak]";
     }
 }
@@ -457,6 +462,9 @@
 void Style::print(std::ostream* out) const {
     *out << "(style) ";
     if (parent && parent.value().name) {
+        if (parent.value().privateReference) {
+            *out << "*";
+        }
         *out << parent.value().name.value();
     }
     *out << " ["
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index a038282..8e317db 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -43,9 +43,15 @@
 
     /**
      * Whether this value is weak and can be overridden without
-     * warning or error. Default for base class is false.
+     * warning or error. Default is false.
      */
-    virtual bool isWeak() const;
+    bool isWeak() const {
+        return mWeak;
+    }
+
+    void setWeak(bool val) {
+        mWeak = val;
+    }
 
     /**
      * Returns the source where this value was defined.
@@ -95,6 +101,7 @@
 protected:
     Source mSource;
     std::u16string mComment;
+    bool mWeak = false;
 };
 
 /**
@@ -159,7 +166,7 @@
  * An ID resource. Has no real value, just a place holder.
  */
 struct Id : public BaseItem<Id> {
-    bool isWeak() const override;
+    Id() { mWeak = true; }
     bool flatten(android::Res_value* out) const override;
     Id* clone(StringPool* newPool) const override;
     void print(std::ostream* out) const override;
@@ -185,9 +192,17 @@
 
     String(const StringPool::Ref& ref);
 
+    // Whether the string is marked as translateable. This does not persist when flattened.
+    // It is only used during compilation phase.
+    void setTranslateable(bool val);
+    bool isTranslateable() const;
+
     bool flatten(android::Res_value* outValue) const override;
     String* clone(StringPool* newPool) const override;
     void print(std::ostream* out) const override;
+
+private:
+    bool mTranslateable;
 };
 
 struct StyledString : public BaseItem<StyledString> {
@@ -195,9 +210,17 @@
 
     StyledString(const StringPool::StyleRef& ref);
 
+    // Whether the string is marked as translateable. This does not persist when flattened.
+    // It is only used during compilation phase.
+    void setTranslateable(bool val);
+    bool isTranslateable() const;
+
     bool flatten(android::Res_value* outValue) const override;
     StyledString* clone(StringPool* newPool) const override;
     void print(std::ostream* out) const override;
+
+private:
+    bool mTranslateable;
 };
 
 struct FileReference : public BaseItem<FileReference> {
@@ -232,7 +255,6 @@
         uint32_t value;
     };
 
-	bool weak;
     uint32_t typeMask;
     int32_t minInt;
     int32_t maxInt;
@@ -240,7 +262,6 @@
 
     Attribute(bool w, uint32_t t = 0u);
 
-    bool isWeak() const override;
     Attribute* clone(StringPool* newPool) const override;
     void printMask(std::ostream* out) const;
     void print(std::ostream* out) const override;
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 8552f47..aadb00b 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
+#include "StringPool.h"
 #include "util/BigBuffer.h"
 #include "util/StringPiece.h"
-#include "StringPool.h"
 #include "util/Util.h"
 
 #include <algorithm>
@@ -342,7 +342,14 @@
 
             // Encode the actual UTF16 string length.
             data = encodeLength(data, entry->value.size());
-            strncpy16(data, entry->value.data(), entry->value.size());
+            const size_t byteLength = entry->value.size() * sizeof(char16_t);
+
+            // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size())
+            // truncates the string.
+            memcpy(data, entry->value.data(), byteLength);
+
+            // The null-terminating character is already here due to the block of data being set
+            // to 0s on allocation.
         }
     }
 
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index c722fbe..e93c2fba 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -180,6 +180,22 @@
     ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
 }
 
+TEST(StringPoolTest, FlattenOddCharactersUtf16) {
+    StringPool pool;
+    pool.makeRef(u"\u093f");
+    BigBuffer buffer(1024);
+    StringPool::flattenUtf16(&buffer, pool);
+
+    std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+    android::ResStringPool test;
+    ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+    size_t len = 0;
+    const char16_t* str = test.stringAt(0, &len);
+    EXPECT_EQ(1u, len);
+    EXPECT_EQ(u'\u093f', *str);
+    EXPECT_EQ(0u, str[1]);
+}
+
 constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
 
 TEST(StringPoolTest, FlattenUtf8) {
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 90e35d5..689ace6 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -21,6 +21,7 @@
 #include "ResourceTable.h"
 #include "compile/IdAssigner.h"
 #include "compile/Png.h"
+#include "compile/PseudolocaleGenerator.h"
 #include "compile/XmlIdCollector.h"
 #include "flatten/Archive.h"
 #include "flatten/FileExportWriter.h"
@@ -104,7 +105,9 @@
 struct CompileOptions {
     std::string outputPath;
     Maybe<std::string> resDir;
-    Maybe<std::u16string> product;
+    std::vector<std::u16string> products;
+    bool pseudolocalize = false;
+    bool legacyMode = false;
     bool verbose = false;
 };
 
@@ -114,7 +117,11 @@
     if (!data.configStr.empty()) {
         name << "-" << data.configStr;
     }
-    name << "_" << data.name << "." << data.extension << ".flat";
+    name << "_" << data.name;
+    if (!data.extension.empty()) {
+        name << "." << data.extension;
+    }
+    name << ".flat";
     return name.str();
 }
 
@@ -189,7 +196,8 @@
         xml::XmlPullParser xmlParser(fin);
 
         ResourceParserOptions parserOptions;
-        parserOptions.product = options.product;
+        parserOptions.products = options.products;
+        parserOptions.errorOnPositionalArguments = !options.legacyMode;
 
         // If the filename includes donottranslate, then the default translatable is false.
         parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos;
@@ -203,6 +211,16 @@
         fin.close();
     }
 
+    if (options.pseudolocalize) {
+        // Generate pseudo-localized strings (en-XA and ar-XB).
+        // These are created as weak symbols, and are only generated from default configuration
+        // strings and plurals.
+        PseudolocaleGenerator pseudolocaleGenerator;
+        if (!pseudolocaleGenerator.consume(context, &table)) {
+            return false;
+        }
+    }
+
     // Ensure we have the compilation package at least.
     table.createPackage(context->getCompilationPackage());
 
@@ -372,16 +390,26 @@
     fileExportWriter.getChunkHeader()->size =
             util::hostToDevice32(buffer.size() + f.value().getDataLength());
 
-    if (writer->writeEntry(buffer)) {
-        if (writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
-            if (writer->finishEntry()) {
-                return true;
-            }
+    if (!writer->writeEntry(buffer)) {
+        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+        return false;
+    }
+
+    // Only write if we have something to write. This is because mmap fails with length of 0,
+    // but we still want to compile the file to get the resource ID.
+    if (f.value().getDataPtr() && f.value().getDataLength() > 0) {
+        if (!writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
+            context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+            return false;
         }
     }
 
-    context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
-    return false;
+    if (!writer->finishEntry()) {
+        context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+        return false;
+    }
+
+    return true;
 }
 
 class CompileContext : public IAaptContext {
@@ -418,18 +446,25 @@
 int compile(const std::vector<StringPiece>& args) {
     CompileOptions options;
 
-    Maybe<std::string> product;
+    Maybe<std::string> productList;
     Flags flags = Flags()
             .requiredFlag("-o", "Output path", &options.outputPath)
-            .optionalFlag("--product", "Product type to compile", &product)
+            .optionalFlag("--product", "Comma separated list of product types to compile",
+                          &productList)
             .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
+            .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
+                            "(en-XA and ar-XB)", &options.pseudolocalize)
+            .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
+                            &options.legacyMode)
             .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
     if (!flags.parse("aapt2 compile", args, &std::cerr)) {
         return 1;
     }
 
-    if (product) {
-        options.product = util::utf8ToUtf16(product.value());
+    if (productList) {
+        for (StringPiece part : util::tokenize<char>(productList.value(), ',')) {
+            options.products.push_back(util::utf8ToUtf16(part));
+        }
     }
 
     CompileContext context;
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
new file mode 100644
index 0000000..2963d13
--- /dev/null
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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 "ResourceTable.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
+#include "compile/PseudolocaleGenerator.h"
+#include "compile/Pseudolocalizer.h"
+#include "util/Comparators.h"
+
+namespace aapt {
+
+std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
+                                                         Pseudolocalizer::Method method,
+                                                         StringPool* pool) {
+    Pseudolocalizer localizer(method);
+
+    const StringPiece16 originalText = *string->value->str;
+
+    StyleString localized;
+
+    // Copy the spans. We will update their offsets when we localize.
+    localized.spans.reserve(string->value->spans.size());
+    for (const StringPool::Span& span : string->value->spans) {
+        localized.spans.push_back(Span{ *span.name, span.firstChar, span.lastChar });
+    }
+
+    // The ranges are all represented with a single value. This is the start of one range and
+    // end of another.
+    struct Range {
+        size_t start;
+
+        // Once the new string is localized, these are the pointers to the spans to adjust.
+        // Since this struct represents the start of one range and end of another, we have
+        // the two pointers respectively.
+        uint32_t* updateStart;
+        uint32_t* updateEnd;
+    };
+
+    auto cmp = [](const Range& r, size_t index) -> bool {
+        return r.start < index;
+    };
+
+    // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
+    // The ranges are the spaces in between. In this example, with a total string length of 9,
+    // the vector represents: (0,1], (2,4], (5,6], (7,9]
+    //
+    std::vector<Range> ranges;
+    ranges.push_back(Range{ 0 });
+    ranges.push_back(Range{ originalText.size() - 1 });
+    for (size_t i = 0; i < string->value->spans.size(); i++) {
+        const StringPool::Span& span = string->value->spans[i];
+
+        // Insert or update the Range marker for the start of this span.
+        auto iter = std::lower_bound(ranges.begin(), ranges.end(), span.firstChar, cmp);
+        if (iter != ranges.end() && iter->start == span.firstChar) {
+            iter->updateStart = &localized.spans[i].firstChar;
+        } else {
+            ranges.insert(iter,
+                          Range{ span.firstChar, &localized.spans[i].firstChar, nullptr });
+        }
+
+        // Insert or update the Range marker for the end of this span.
+        iter = std::lower_bound(ranges.begin(), ranges.end(), span.lastChar, cmp);
+        if (iter != ranges.end() && iter->start == span.lastChar) {
+            iter->updateEnd = &localized.spans[i].lastChar;
+        } else {
+            ranges.insert(iter,
+                          Range{ span.lastChar, nullptr, &localized.spans[i].lastChar });
+        }
+    }
+
+    localized.str += localizer.start();
+
+    // Iterate over the ranges and localize each section.
+    for (size_t i = 0; i < ranges.size(); i++) {
+        const size_t start = ranges[i].start;
+        size_t len = originalText.size() - start;
+        if (i + 1 < ranges.size()) {
+            len = ranges[i + 1].start - start;
+        }
+
+        if (ranges[i].updateStart) {
+            *ranges[i].updateStart = localized.str.size();
+        }
+
+        if (ranges[i].updateEnd) {
+            *ranges[i].updateEnd = localized.str.size();
+        }
+
+        localized.str += localizer.text(originalText.substr(start, len));
+    }
+
+    localized.str += localizer.end();
+
+    std::unique_ptr<StyledString> localizedString = util::make_unique<StyledString>(
+            pool->makeRef(localized));
+    localizedString->setSource(string->getSource());
+    return localizedString;
+}
+
+namespace {
+
+struct Visitor : public RawValueVisitor {
+    StringPool* mPool;
+    Pseudolocalizer::Method mMethod;
+    Pseudolocalizer mLocalizer;
+
+    // Either value or item will be populated upon visiting the value.
+    std::unique_ptr<Value> mValue;
+    std::unique_ptr<Item> mItem;
+
+    Visitor(StringPool* pool, Pseudolocalizer::Method method) :
+            mPool(pool), mMethod(method), mLocalizer(method) {
+    }
+
+    void visit(Array* array) override {
+        std::unique_ptr<Array> localized = util::make_unique<Array>();
+        localized->items.resize(array->items.size());
+        for (size_t i = 0; i < array->items.size(); i++) {
+            Visitor subVisitor(mPool, mMethod);
+            array->items[i]->accept(&subVisitor);
+            if (subVisitor.mItem) {
+                localized->items[i] = std::move(subVisitor.mItem);
+            } else {
+                localized->items[i] = std::unique_ptr<Item>(array->items[i]->clone(mPool));
+            }
+        }
+        localized->setSource(array->getSource());
+        localized->setWeak(true);
+        mValue = std::move(localized);
+    }
+
+    void visit(Plural* plural) override {
+        std::unique_ptr<Plural> localized = util::make_unique<Plural>();
+        for (size_t i = 0; i < plural->values.size(); i++) {
+            Visitor subVisitor(mPool, mMethod);
+            if (plural->values[i]) {
+                plural->values[i]->accept(&subVisitor);
+                if (subVisitor.mValue) {
+                    localized->values[i] = std::move(subVisitor.mItem);
+                } else {
+                    localized->values[i] = std::unique_ptr<Item>(plural->values[i]->clone(mPool));
+                }
+            }
+        }
+        localized->setSource(plural->getSource());
+        localized->setWeak(true);
+        mValue = std::move(localized);
+    }
+
+    void visit(String* string) override {
+        if (!string->isTranslateable()) {
+            return;
+        }
+
+        std::u16string result = mLocalizer.start() + mLocalizer.text(*string->value) +
+                mLocalizer.end();
+        std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result));
+        localized->setSource(string->getSource());
+        localized->setWeak(true);
+        mItem = std::move(localized);
+    }
+
+    void visit(StyledString* string) override {
+        if (!string->isTranslateable()) {
+            return;
+        }
+
+        mItem = pseudolocalizeStyledString(string, mMethod, mPool);
+        mItem->setWeak(true);
+    }
+};
+
+ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
+                                              Pseudolocalizer::Method m) {
+    ConfigDescription modified = base;
+    switch (m) {
+    case Pseudolocalizer::Method::kAccent:
+        modified.language[0] = 'e';
+        modified.language[1] = 'n';
+        modified.country[0] = 'X';
+        modified.country[1] = 'A';
+        break;
+
+    case Pseudolocalizer::Method::kBidi:
+        modified.language[0] = 'a';
+        modified.language[1] = 'r';
+        modified.country[0] = 'X';
+        modified.country[1] = 'B';
+        break;
+    default:
+        break;
+    }
+    return modified;
+}
+
+void pseudolocalizeIfNeeded(std::vector<ResourceConfigValue>* configValues,
+                            Pseudolocalizer::Method method, StringPool* pool, Value* value) {
+    Visitor visitor(pool, method);
+    value->accept(&visitor);
+
+    std::unique_ptr<Value> localizedValue;
+    if (visitor.mValue) {
+        localizedValue = std::move(visitor.mValue);
+    } else if (visitor.mItem) {
+        localizedValue = std::move(visitor.mItem);
+    }
+
+    if (localizedValue) {
+        ConfigDescription pseudolocalizedConfig = modifyConfigForPseudoLocale(ConfigDescription{},
+                                                                              method);
+        auto iter = std::lower_bound(configValues->begin(), configValues->end(),
+                                     pseudolocalizedConfig, cmp::lessThanConfig);
+        if (iter == configValues->end() || iter->config != pseudolocalizedConfig) {
+            // The pseudolocalized config doesn't exist, add it.
+            configValues->insert(iter, ResourceConfigValue{ pseudolocalizedConfig,
+                                                            std::move(localizedValue) });
+        }
+    }
+}
+
+} // namespace
+
+bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table) {
+    for (auto& package : table->packages) {
+        for (auto& type : package->types) {
+            for (auto& entry : type->entries) {
+                auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
+                                             ConfigDescription{}, cmp::lessThanConfig);
+                if (iter != entry->values.end() && iter->config == ConfigDescription{}) {
+                    // Only pseudolocalize the default configuration.
+
+                    // The iterator will be invalidated, so grab a pointer to the value.
+                    Value* originalValue = iter->value.get();
+
+                    pseudolocalizeIfNeeded(&entry->values, Pseudolocalizer::Method::kAccent,
+                                           &table->stringPool, originalValue);
+                    pseudolocalizeIfNeeded(&entry->values, Pseudolocalizer::Method::kBidi,
+                                           &table->stringPool, originalValue);
+                }
+            }
+        }
+    }
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.h b/tools/aapt2/compile/PseudolocaleGenerator.h
new file mode 100644
index 0000000..4fbc516
--- /dev/null
+++ b/tools/aapt2/compile/PseudolocaleGenerator.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_COMPILE_PSEUDOLOCALEGENERATOR_H
+#define AAPT_COMPILE_PSEUDOLOCALEGENERATOR_H
+
+#include "StringPool.h"
+#include "compile/Pseudolocalizer.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
+                                                         Pseudolocalizer::Method method,
+                                                         StringPool* pool);
+
+struct PseudolocaleGenerator : public IResourceTableConsumer {
+    bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_COMPILE_PSEUDOLOCALEGENERATOR_H */
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
new file mode 100644
index 0000000..4cb6ea2
--- /dev/null
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "compile/PseudolocaleGenerator.h"
+#include "test/Builders.h"
+#include "test/Common.h"
+#include "test/Context.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
+    StringPool pool;
+    StyleString originalStyle;
+    originalStyle.str = u"Hello world!";
+    originalStyle.spans = { Span{ u"b", 2, 3 }, Span{ u"b", 6, 7 }, Span{ u"i", 1, 10 } };
+
+    std::unique_ptr<StyledString> newString = pseudolocalizeStyledString(
+            util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
+            Pseudolocalizer::Method::kNone, &pool);
+
+    EXPECT_EQ(originalStyle.str, *newString->value->str);
+    ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+
+    EXPECT_EQ(2u, newString->value->spans[0].firstChar);
+    EXPECT_EQ(3u, newString->value->spans[0].lastChar);
+    EXPECT_EQ(std::u16string(u"b"), *newString->value->spans[0].name);
+
+    EXPECT_EQ(6u, newString->value->spans[1].firstChar);
+    EXPECT_EQ(7u, newString->value->spans[1].lastChar);
+    EXPECT_EQ(std::u16string(u"b"), *newString->value->spans[1].name);
+
+    EXPECT_EQ(1u, newString->value->spans[2].firstChar);
+    EXPECT_EQ(10u, newString->value->spans[2].lastChar);
+    EXPECT_EQ(std::u16string(u"i"), *newString->value->spans[2].name);
+
+    originalStyle.spans.push_back(Span{ u"em", 0, 11u });
+
+    newString = pseudolocalizeStyledString(
+            util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
+            Pseudolocalizer::Method::kAccent, &pool);
+
+    EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str);
+    ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+
+    EXPECT_EQ(3u, newString->value->spans[0].firstChar);
+    EXPECT_EQ(4u, newString->value->spans[0].lastChar);
+
+    EXPECT_EQ(7u, newString->value->spans[1].firstChar);
+    EXPECT_EQ(8u, newString->value->spans[1].lastChar);
+
+    EXPECT_EQ(2u, newString->value->spans[2].firstChar);
+    EXPECT_EQ(11u, newString->value->spans[2].lastChar);
+
+    EXPECT_EQ(1u, newString->value->spans[3].firstChar);
+    EXPECT_EQ(12u, newString->value->spans[3].lastChar);
+}
+
+TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) {
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .addString(u"@android:string/one", u"one")
+            .addString(u"@android:string/two", ResourceId{}, test::parseConfigOrDie("en"), u"two")
+            .addString(u"@android:string/three", u"three")
+            .addString(u"@android:string/three", ResourceId{}, test::parseConfigOrDie("en-rXA"),
+                       u"three")
+            .addString(u"@android:string/four", u"four")
+            .build();
+
+    String* val = test::getValue<String>(table.get(), u"@android:string/four");
+    val->setTranslateable(false);
+
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    PseudolocaleGenerator generator;
+    ASSERT_TRUE(generator.consume(context.get(), table.get()));
+
+    // Normal pseudolocalization should take place.
+    ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/one",
+                                                       test::parseConfigOrDie("en-rXA")));
+    ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/one",
+                                                       test::parseConfigOrDie("ar-rXB")));
+
+    // No default config for android:string/two, so no pseudlocales should exist.
+    ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/two",
+                                                       test::parseConfigOrDie("en-rXA")));
+    ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/two",
+                                                       test::parseConfigOrDie("ar-rXB")));
+
+
+    // Check that we didn't override manual pseudolocalization.
+    val = test::getValueForConfig<String>(table.get(), u"@android:string/three",
+                                          test::parseConfigOrDie("en-rXA"));
+    ASSERT_NE(nullptr, val);
+    EXPECT_EQ(std::u16string(u"three"), *val->value);
+
+    ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/three",
+                                                       test::parseConfigOrDie("ar-rXB")));
+
+    // Check that four's translateable marker was honored.
+    ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/four",
+                                                       test::parseConfigOrDie("en-rXA")));
+    ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/four",
+                                                       test::parseConfigOrDie("ar-rXB")));
+
+}
+
+} // namespace aapt
+
diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp
new file mode 100644
index 0000000..eae52d7
--- /dev/null
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2015 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 "compile/Pseudolocalizer.h"
+#include "util/Util.h"
+
+namespace aapt {
+
+// String basis to generate expansion
+static const std::u16string k_expansion_string = u"one two three "
+        "four five six seven eight nine ten eleven twelve thirteen "
+        "fourteen fiveteen sixteen seventeen nineteen twenty";
+
+// Special unicode characters to override directionality of the words
+static const std::u16string k_rlm = u"\u200f";
+static const std::u16string k_rlo = u"\u202e";
+static const std::u16string k_pdf = u"\u202c";
+
+// Placeholder marks
+static const std::u16string k_placeholder_open = u"\u00bb";
+static const std::u16string k_placeholder_close = u"\u00ab";
+
+static const char16_t k_arg_start = u'{';
+static const char16_t k_arg_end = u'}';
+
+class PseudoMethodNone : public PseudoMethodImpl {
+public:
+    std::u16string text(const StringPiece16& text) override { return text.toString(); }
+    std::u16string placeholder(const StringPiece16& text) override { return text.toString(); }
+};
+
+class PseudoMethodBidi : public PseudoMethodImpl {
+public:
+    std::u16string text(const StringPiece16& text) override;
+    std::u16string placeholder(const StringPiece16& text) override;
+};
+
+class PseudoMethodAccent : public PseudoMethodImpl {
+public:
+    PseudoMethodAccent() : mDepth(0), mWordCount(0), mLength(0) {}
+    std::u16string start() override;
+    std::u16string end() override;
+    std::u16string text(const StringPiece16& text) override;
+    std::u16string placeholder(const StringPiece16& text) override;
+private:
+    size_t mDepth;
+    size_t mWordCount;
+    size_t mLength;
+};
+
+Pseudolocalizer::Pseudolocalizer(Method method) : mLastDepth(0) {
+    setMethod(method);
+}
+
+void Pseudolocalizer::setMethod(Method method) {
+    switch (method) {
+    case Method::kNone:
+        mImpl = util::make_unique<PseudoMethodNone>();
+        break;
+    case Method::kAccent:
+        mImpl = util::make_unique<PseudoMethodAccent>();
+        break;
+    case Method::kBidi:
+        mImpl = util::make_unique<PseudoMethodBidi>();
+        break;
+    }
+}
+
+std::u16string Pseudolocalizer::text(const StringPiece16& text) {
+    std::u16string out;
+    size_t depth = mLastDepth;
+    size_t lastpos, pos;
+    const size_t length = text.size();
+    const char16_t* str = text.data();
+    bool escaped = false;
+    for (lastpos = pos = 0; pos < length; pos++) {
+        char16_t c = str[pos];
+        if (escaped) {
+            escaped = false;
+            continue;
+        }
+        if (c == '\'') {
+            escaped = true;
+            continue;
+        }
+
+        if (c == k_arg_start) {
+            depth++;
+        } else if (c == k_arg_end && depth) {
+            depth--;
+        }
+
+        if (mLastDepth != depth || pos == length - 1) {
+            bool pseudo = ((mLastDepth % 2) == 0);
+            size_t nextpos = pos;
+            if (!pseudo || depth == mLastDepth) {
+                nextpos++;
+            }
+            size_t size = nextpos - lastpos;
+            if (size) {
+                std::u16string chunk = text.substr(lastpos, size).toString();
+                if (pseudo) {
+                    chunk = mImpl->text(chunk);
+                } else if (str[lastpos] == k_arg_start && str[nextpos - 1] == k_arg_end) {
+                    chunk = mImpl->placeholder(chunk);
+                }
+                out.append(chunk);
+            }
+            if (pseudo && depth < mLastDepth) { // End of message
+                out.append(mImpl->end());
+            } else if (!pseudo && depth > mLastDepth) { // Start of message
+                out.append(mImpl->start());
+            }
+            lastpos = nextpos;
+            mLastDepth = depth;
+        }
+    }
+    return out;
+}
+
+static const char16_t* pseudolocalizeChar(const char16_t c) {
+    switch (c) {
+        case 'a':   return u"\u00e5";
+        case 'b':   return u"\u0253";
+        case 'c':   return u"\u00e7";
+        case 'd':   return u"\u00f0";
+        case 'e':   return u"\u00e9";
+        case 'f':   return u"\u0192";
+        case 'g':   return u"\u011d";
+        case 'h':   return u"\u0125";
+        case 'i':   return u"\u00ee";
+        case 'j':   return u"\u0135";
+        case 'k':   return u"\u0137";
+        case 'l':   return u"\u013c";
+        case 'm':   return u"\u1e3f";
+        case 'n':   return u"\u00f1";
+        case 'o':   return u"\u00f6";
+        case 'p':   return u"\u00fe";
+        case 'q':   return u"\u0051";
+        case 'r':   return u"\u0155";
+        case 's':   return u"\u0161";
+        case 't':   return u"\u0163";
+        case 'u':   return u"\u00fb";
+        case 'v':   return u"\u0056";
+        case 'w':   return u"\u0175";
+        case 'x':   return u"\u0445";
+        case 'y':   return u"\u00fd";
+        case 'z':   return u"\u017e";
+        case 'A':   return u"\u00c5";
+        case 'B':   return u"\u03b2";
+        case 'C':   return u"\u00c7";
+        case 'D':   return u"\u00d0";
+        case 'E':   return u"\u00c9";
+        case 'G':   return u"\u011c";
+        case 'H':   return u"\u0124";
+        case 'I':   return u"\u00ce";
+        case 'J':   return u"\u0134";
+        case 'K':   return u"\u0136";
+        case 'L':   return u"\u013b";
+        case 'M':   return u"\u1e3e";
+        case 'N':   return u"\u00d1";
+        case 'O':   return u"\u00d6";
+        case 'P':   return u"\u00de";
+        case 'Q':   return u"\u0071";
+        case 'R':   return u"\u0154";
+        case 'S':   return u"\u0160";
+        case 'T':   return u"\u0162";
+        case 'U':   return u"\u00db";
+        case 'V':   return u"\u03bd";
+        case 'W':   return u"\u0174";
+        case 'X':   return u"\u00d7";
+        case 'Y':   return u"\u00dd";
+        case 'Z':   return u"\u017d";
+        case '!':   return u"\u00a1";
+        case '?':   return u"\u00bf";
+        case '$':   return u"\u20ac";
+        default:    return NULL;
+    }
+}
+
+static bool isPossibleNormalPlaceholderEnd(const char16_t c) {
+    switch (c) {
+        case 's': return true;
+        case 'S': return true;
+        case 'c': return true;
+        case 'C': return true;
+        case 'd': return true;
+        case 'o': return true;
+        case 'x': return true;
+        case 'X': return true;
+        case 'f': return true;
+        case 'e': return true;
+        case 'E': return true;
+        case 'g': return true;
+        case 'G': return true;
+        case 'a': return true;
+        case 'A': return true;
+        case 'b': return true;
+        case 'B': return true;
+        case 'h': return true;
+        case 'H': return true;
+        case '%': return true;
+        case 'n': return true;
+        default:  return false;
+    }
+}
+
+static std::u16string pseudoGenerateExpansion(const unsigned int length) {
+    std::u16string result = k_expansion_string;
+    const char16_t* s = result.data();
+    if (result.size() < length) {
+        result += u" ";
+        result += pseudoGenerateExpansion(length - result.size());
+    } else {
+        int ext = 0;
+        // Should contain only whole words, so looking for a space
+        for (unsigned int i = length + 1; i < result.size(); ++i) {
+            ++ext;
+            if (s[i] == ' ') {
+                break;
+            }
+        }
+        result = result.substr(0, length + ext);
+    }
+    return result;
+}
+
+std::u16string PseudoMethodAccent::start() {
+    std::u16string result;
+    if (mDepth == 0) {
+        result = u"[";
+    }
+    mWordCount = mLength = 0;
+    mDepth++;
+    return result;
+}
+
+std::u16string PseudoMethodAccent::end() {
+    std::u16string result;
+    if (mLength) {
+        result += u" ";
+        result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2);
+    }
+    mWordCount = mLength = 0;
+    mDepth--;
+    if (mDepth == 0) {
+        result += u"]";
+    }
+    return result;
+}
+
+/**
+ * Converts characters so they look like they've been localized.
+ *
+ * Note: This leaves placeholder syntax untouched.
+ */
+std::u16string PseudoMethodAccent::text(const StringPiece16& source)
+{
+    const char16_t* s = source.data();
+    std::u16string result;
+    const size_t I = source.size();
+    bool lastspace = true;
+    for (size_t i = 0; i < I; i++) {
+        char16_t c = s[i];
+        if (c == '%') {
+            // Placeholder syntax, no need to pseudolocalize
+            std::u16string chunk;
+            bool end = false;
+            chunk.append(&c, 1);
+            while (!end && i < I) {
+                ++i;
+                c = s[i];
+                chunk.append(&c, 1);
+                if (isPossibleNormalPlaceholderEnd(c)) {
+                    end = true;
+                } else if (c == 't') {
+                    ++i;
+                    c = s[i];
+                    chunk.append(&c, 1);
+                    end = true;
+                }
+            }
+            // Treat chunk as a placeholder unless it ends with %.
+            result += ((c == '%') ? chunk : placeholder(chunk));
+        } else if (c == '<' || c == '&') {
+            // html syntax, no need to pseudolocalize
+            bool tag_closed = false;
+            while (!tag_closed && i < I) {
+                if (c == '&') {
+                    std::u16string escapeText;
+                    escapeText.append(&c, 1);
+                    bool end = false;
+                    size_t htmlCodePos = i;
+                    while (!end && htmlCodePos < I) {
+                        ++htmlCodePos;
+                        c = s[htmlCodePos];
+                        escapeText.append(&c, 1);
+                        // Valid html code
+                        if (c == ';') {
+                            end = true;
+                            i = htmlCodePos;
+                        }
+                        // Wrong html code
+                        else if (!((c == '#' ||
+                                 (c >= 'a' && c <= 'z') ||
+                                 (c >= 'A' && c <= 'Z') ||
+                                 (c >= '0' && c <= '9')))) {
+                            end = true;
+                        }
+                    }
+                    result += escapeText;
+                    if (escapeText != u"&lt;") {
+                        tag_closed = true;
+                    }
+                    continue;
+                }
+                if (c == '>') {
+                    tag_closed = true;
+                    result.append(&c, 1);
+                    continue;
+                }
+                result.append(&c, 1);
+                i++;
+                c = s[i];
+            }
+        } else {
+            // This is a pure text that should be pseudolocalized
+            const char16_t* p = pseudolocalizeChar(c);
+            if (p != nullptr) {
+                result += p;
+            } else {
+                bool space = util::isspace16(c);
+                if (lastspace && !space) {
+                    mWordCount++;
+                }
+                lastspace = space;
+                result.append(&c, 1);
+            }
+            // Count only pseudolocalizable chars and delimiters
+            mLength++;
+        }
+    }
+    return result;
+}
+
+std::u16string PseudoMethodAccent::placeholder(const StringPiece16& source) {
+    // Surround a placeholder with brackets
+    return k_placeholder_open + source.toString() + k_placeholder_close;
+}
+
+std::u16string PseudoMethodBidi::text(const StringPiece16& source) {
+    const char16_t* s = source.data();
+    std::u16string result;
+    bool lastspace = true;
+    bool space = true;
+    for (size_t i = 0; i < source.size(); i++) {
+        char16_t c = s[i];
+        space = util::isspace16(c);
+        if (lastspace && !space) {
+            // Word start
+            result += k_rlm + k_rlo;
+        } else if (!lastspace && space) {
+            // Word end
+            result += k_pdf + k_rlm;
+        }
+        lastspace = space;
+        result.append(&c, 1);
+    }
+    if (!lastspace) {
+        // End of last word
+        result += k_pdf + k_rlm;
+    }
+    return result;
+}
+
+std::u16string PseudoMethodBidi::placeholder(const StringPiece16& source) {
+    // Surround a placeholder with directionality change sequence
+    return k_rlm + k_rlo + source.toString() + k_pdf + k_rlm;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h
new file mode 100644
index 0000000..8818c17
--- /dev/null
+++ b/tools/aapt2/compile/Pseudolocalizer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef AAPT_COMPILE_PSEUDOLOCALIZE_H
+#define AAPT_COMPILE_PSEUDOLOCALIZE_H
+
+#include "ResourceValues.h"
+#include "StringPool.h"
+#include "util/StringPiece.h"
+
+#include <android-base/macros.h>
+#include <memory>
+
+namespace aapt {
+
+class PseudoMethodImpl {
+public:
+    virtual ~PseudoMethodImpl() {}
+    virtual std::u16string start() { return {}; }
+    virtual std::u16string end() { return {}; }
+    virtual std::u16string text(const StringPiece16& text) = 0;
+    virtual std::u16string placeholder(const StringPiece16& text) = 0;
+};
+
+class Pseudolocalizer {
+public:
+    enum class Method {
+        kNone,
+        kAccent,
+        kBidi,
+    };
+
+    Pseudolocalizer(Method method);
+    void setMethod(Method method);
+    std::u16string start() { return mImpl->start(); }
+    std::u16string end() { return mImpl->end(); }
+    std::u16string text(const StringPiece16& text);
+private:
+    std::unique_ptr<PseudoMethodImpl> mImpl;
+    size_t mLastDepth;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_COMPILE_PSEUDOLOCALIZE_H */
diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp
new file mode 100644
index 0000000..b0bc2c1
--- /dev/null
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2015 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 "compile/Pseudolocalizer.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+// In this context, 'Axis' represents a particular field in the configuration,
+// such as language or density.
+
+static ::testing::AssertionResult simpleHelper(const char* input, const char* expected,
+                                               Pseudolocalizer::Method method) {
+    Pseudolocalizer pseudo(method);
+    std::string result = util::utf16ToUtf8(
+            pseudo.start() + pseudo.text(util::utf8ToUtf16(input)) + pseudo.end());
+    if (StringPiece(expected) != result) {
+        return ::testing::AssertionFailure() << expected << " != " << result;
+    }
+    return ::testing::AssertionSuccess();
+}
+
+static ::testing::AssertionResult compoundHelper(const char* in1, const char* in2, const char *in3,
+                                                 const char* expected,
+                                                 Pseudolocalizer::Method method) {
+    Pseudolocalizer pseudo(method);
+    std::string result = util::utf16ToUtf8(pseudo.start() +
+                                           pseudo.text(util::utf8ToUtf16(in1)) +
+                                           pseudo.text(util::utf8ToUtf16(in2)) +
+                                           pseudo.text(util::utf8ToUtf16(in3)) +
+                                           pseudo.end());
+    if (StringPiece(expected) != result) {
+        return ::testing::AssertionFailure() << expected << " != " << result;
+    }
+    return ::testing::AssertionSuccess();
+}
+
+TEST(PseudolocalizerTest, NoPseudolocalization) {
+    EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kNone));
+    EXPECT_TRUE(simpleHelper("Hello, world", "Hello, world", Pseudolocalizer::Method::kNone));
+
+    EXPECT_TRUE(compoundHelper("Hello,", " world", "",
+                               "Hello, world", Pseudolocalizer::Method::kNone));
+}
+
+TEST(PseudolocalizerTest, PlaintextAccent) {
+    EXPECT_TRUE(simpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
+    EXPECT_TRUE(simpleHelper("Hello, world",
+                             "[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent));
+
+    EXPECT_TRUE(simpleHelper("Hello, %1d",
+                             "[Ĥéļļö, »%1d« one two]", Pseudolocalizer::Method::kAccent));
+
+    EXPECT_TRUE(simpleHelper("Battery %1d%%",
+                             "[βåţţéŕý »%1d«%% one two]", Pseudolocalizer::Method::kAccent));
+
+    EXPECT_TRUE(compoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent));
+    EXPECT_TRUE(compoundHelper("Hello,", " world", "",
+                               "[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent));
+}
+
+TEST(PseudolocalizerTest, PlaintextBidi) {
+    EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kBidi));
+    EXPECT_TRUE(simpleHelper("word",
+                             "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
+                             Pseudolocalizer::Method::kBidi));
+    EXPECT_TRUE(simpleHelper("  word  ",
+                             "  \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f  ",
+                             Pseudolocalizer::Method::kBidi));
+    EXPECT_TRUE(simpleHelper("  word  ",
+                             "  \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f  ",
+                             Pseudolocalizer::Method::kBidi));
+    EXPECT_TRUE(simpleHelper("hello\n  world\n",
+                             "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
+                                     "  \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+                             Pseudolocalizer::Method::kBidi));
+    EXPECT_TRUE(compoundHelper("hello", "\n ", " world\n",
+                               "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
+                                       "  \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+                               Pseudolocalizer::Method::kBidi));
+}
+
+TEST(PseudolocalizerTest, SimpleICU) {
+    // Single-fragment messages
+    EXPECT_TRUE(simpleHelper("{placeholder}", "[»{placeholder}«]",
+                             Pseudolocalizer::Method::kAccent));
+    EXPECT_TRUE(simpleHelper("{USER} is offline",
+                             "[»{USER}« îš öƒƒļîñé one two]", Pseudolocalizer::Method::kAccent));
+    EXPECT_TRUE(simpleHelper("Copy from {path1} to {path2}",
+                             "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
+                             Pseudolocalizer::Method::kAccent));
+    EXPECT_TRUE(simpleHelper("Today is {1,date} {1,time}",
+                             "[Ţöðåý îš »{1,date}« »{1,time}« one two]",
+                             Pseudolocalizer::Method::kAccent));
+
+    // Multi-fragment messages
+    EXPECT_TRUE(compoundHelper("{USER}", " ", "is offline",
+                               "[»{USER}« îš öƒƒļîñé one two]",
+                               Pseudolocalizer::Method::kAccent));
+    EXPECT_TRUE(compoundHelper("Copy from ", "{path1}", " to {path2}",
+                               "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
+                               Pseudolocalizer::Method::kAccent));
+}
+
+TEST(PseudolocalizerTest, ICUBidi) {
+    // Single-fragment messages
+    EXPECT_TRUE(simpleHelper("{placeholder}",
+                             "\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
+                             Pseudolocalizer::Method::kBidi));
+    EXPECT_TRUE(simpleHelper(
+            "{COUNT, plural, one {one} other {other}}",
+            "{COUNT, plural, " \
+                    "one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} " \
+                    "other {\xe2\x80\x8f\xE2\x80\xaeother\xE2\x80\xac\xe2\x80\x8f}}",
+            Pseudolocalizer::Method::kBidi));
+}
+
+TEST(PseudolocalizerTest, Escaping) {
+    // Single-fragment messages
+    EXPECT_TRUE(simpleHelper("'{USER'} is offline",
+                             "['{ÛŠÉŔ'} îš öƒƒļîñé one two three]",
+                             Pseudolocalizer::Method::kAccent));
+
+    // Multi-fragment messages
+    EXPECT_TRUE(compoundHelper("'{USER}", " ", "''is offline",
+                               "['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]",
+                               Pseudolocalizer::Method::kAccent));
+}
+
+TEST(PseudolocalizerTest, PluralsAndSelects) {
+    EXPECT_TRUE(simpleHelper(
+            "{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
+            "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
+                     "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
+            Pseudolocalizer::Method::kAccent));
+
+    EXPECT_TRUE(simpleHelper(
+            "Distance is {COUNT, plural, one {# mile} other {# miles}}",
+            "[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} " \
+                                 "other {# ḿîļéš one two}}]",
+            Pseudolocalizer::Method::kAccent));
+
+    EXPECT_TRUE(simpleHelper(
+            "{1, select, female {{1} added you} " \
+                    "male {{1} added you} other {{1} added you}}",
+            "[{1, select, female {»{1}« åððéð ýöû one two} " \
+                    "male {»{1}« åððéð ýöû one two} other {»{1}« åððéð ýöû one two}}]",
+            Pseudolocalizer::Method::kAccent));
+
+    EXPECT_TRUE(compoundHelper(
+            "{COUNT, plural, one {Delete a file} " \
+                    "other {Delete ", "{COUNT}", " files}}",
+            "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
+                    "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
+            Pseudolocalizer::Method::kAccent));
+}
+
+TEST(PseudolocalizerTest, NestedICU) {
+    EXPECT_TRUE(simpleHelper(
+            "{person, select, " \
+                "female {" \
+                    "{num_circles, plural," \
+                        "=0{{person} didn't add you to any of her circles.}" \
+                        "=1{{person} added you to one of her circles.}" \
+                        "other{{person} added you to her # circles.}}}" \
+                "male {" \
+                    "{num_circles, plural," \
+                        "=0{{person} didn't add you to any of his circles.}" \
+                        "=1{{person} added you to one of his circles.}" \
+                        "other{{person} added you to his # circles.}}}" \
+                "other {" \
+                    "{num_circles, plural," \
+                        "=0{{person} didn't add you to any of their circles.}" \
+                        "=1{{person} added you to one of their circles.}" \
+                        "other{{person} added you to their # circles.}}}}",
+            "[{person, select, " \
+                "female {" \
+                    "{num_circles, plural," \
+                        "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥéŕ çîŕçļéš." \
+                            " one two three four five}" \
+                        "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥéŕ çîŕçļéš." \
+                            " one two three four}" \
+                        "other{»{person}« åððéð ýöû ţö ĥéŕ # çîŕçļéš." \
+                            " one two three four}}}" \
+                "male {" \
+                    "{num_circles, plural," \
+                        "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥîš çîŕçļéš." \
+                            " one two three four five}" \
+                        "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥîš çîŕçļéš." \
+                            " one two three four}" \
+                        "other{»{person}« åððéð ýöû ţö ĥîš # çîŕçļéš." \
+                            " one two three four}}}" \
+                "other {{num_circles, plural," \
+                    "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ţĥéîŕ çîŕçļéš." \
+                        " one two three four five}" \
+                    "=1{»{person}« åððéð ýöû ţö öñé öƒ ţĥéîŕ çîŕçļéš." \
+                        " one two three four}" \
+                    "other{»{person}« åððéð ýöû ţö ţĥéîŕ # çîŕçļéš." \
+                        " one two three four}}}}]",
+            Pseudolocalizer::Method::kAccent));
+}
+
+TEST(PseudolocalizerTest, RedefineMethod) {
+    Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent);
+    std::u16string result = pseudo.text(u"Hello, ");
+    pseudo.setMethod(Pseudolocalizer::Method::kNone);
+    result += pseudo.text(u"world!");
+    ASSERT_EQ(StringPiece("Ĥéļļö, world!"), util::utf16ToUtf8(result));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index a2f53e1..26d7c2c 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -113,8 +113,7 @@
     bool mUseExtendedChunks;
 
     size_t mEntryCount = 0;
-    Maybe<uint32_t> mParentIdent;
-    Maybe<ResourceNameRef> mParentName;
+    const Reference* mParent = nullptr;
 
     MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer,
                       StringPool* sourcePool, StringPool* commentPool,
@@ -227,13 +226,8 @@
 
     void visit(Style* style) override {
         if (style->parent) {
-            bool privateRef = style->parent.value().privateReference && mUseExtendedChunks;
-            if (!style->parent.value().id || privateRef) {
-                assert(style->parent.value().name && "reference must have a name");
-                mParentName = style->parent.value().name;
-            } else {
-                mParentIdent = style->parent.value().id.value().id;
-            }
+            // Parents are treated a bit differently, so record the existence and move on.
+            mParent = &style->parent.value();
         }
 
         // Sort the style.
@@ -427,11 +421,16 @@
                                       mOptions.useExtendedChunks);
             entry->value->accept(&visitor);
             outEntry->count = util::hostToDevice32(visitor.mEntryCount);
-            if (visitor.mParentName) {
-                mSymbols->addSymbol(visitor.mParentName.value(),
-                                    beforeEntry + offsetof(ResTable_entry_ext, parent));
-            } else if (visitor.mParentIdent) {
-                outEntry->parent.ident = util::hostToDevice32(visitor.mParentIdent.value());
+            if (visitor.mParent) {
+                const bool forceSymbol = visitor.mParent->privateReference &&
+                        mOptions.useExtendedChunks;
+                if (!visitor.mParent->id || forceSymbol) {
+                    assert(visitor.mParent->name && "reference must have a name");
+                    mSymbols->addSymbol(*visitor.mParent,
+                                        beforeEntry + offsetof(ResTable_entry_ext, parent));
+                } else {
+                    outEntry->parent.ident = util::hostToDevice32(visitor.mParent->id.value().id);
+                }
             }
         }
         return true;
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index 9081c55..467e604 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -79,6 +79,21 @@
     size_t mSize;
 };
 
+/**
+ * When mmap fails because the file has length 0, we use the EmptyData to simulate data of length 0.
+ */
+class EmptyData : public IData {
+public:
+    const void* data() const override {
+        static const uint8_t d = 0;
+        return &d;
+    }
+
+    size_t size() const override {
+        return 0u;
+    }
+};
+
 } // namespace io
 } // namespace aapt
 
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 76f87ae..e758d8a4 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -32,7 +32,10 @@
 std::unique_ptr<IData> RegularFile::openAsData() {
     android::FileMap map;
     if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
-        return util::make_unique<MmappedData>(std::move(map.value()));
+        if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
+            return util::make_unique<MmappedData>(std::move(map.value()));
+        }
+        return util::make_unique<EmptyData>();
     }
     return {};
 }
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index c096854..c610bb0 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -227,14 +227,14 @@
 bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
     for (const auto& entry : keepSet.mKeepSet) {
         for (const Source& source : entry.second) {
-            *out << "// Referenced at " << source << "\n";
+            *out << "# Referenced at " << source << "\n";
         }
         *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
     }
 
     for (const auto& entry : keepSet.mKeepMethodSet) {
         for (const Source& source : entry.second) {
-            *out << "// Referenced at " << source << "\n";
+            *out << "# Referenced at " << source << "\n";
         }
         *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
     }
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 652e52f..51b9cec 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -51,16 +51,19 @@
     std::vector<std::string> includePaths;
     std::vector<std::string> overlayFiles;
     Maybe<std::string> generateJavaClassPath;
-    std::set<std::string> extraJavaPackages;
+    Maybe<std::u16string> customJavaPackage;
+    std::set<std::u16string> extraJavaPackages;
     Maybe<std::string> generateProguardRulesPath;
     bool noAutoVersion = false;
     bool staticLib = false;
     bool verbose = false;
     bool outputToDirectory = false;
     bool autoAddOverlay = false;
+    bool doNotCompressAnything = false;
+    std::vector<std::string> extensionsToNotCompress;
     Maybe<std::u16string> privateSymbols;
-    Maybe<std::u16string> minSdkVersionDefault;
-    Maybe<std::u16string> targetSdkVersionDefault;
+    ManifestFixerOptions manifestFixerOptions;
+
 };
 
 struct LinkContext : public IAaptContext {
@@ -186,7 +189,20 @@
         return resFile;
     }
 
-    bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags,
+    uint32_t getCompressionFlags(const StringPiece& str) {
+        if (mOptions.doNotCompressAnything) {
+            return 0;
+        }
+
+        for (const std::string& extension : mOptions.extensionsToNotCompress) {
+            if (util::stringEndsWith<char>(str, extension)) {
+                return 0;
+            }
+        }
+        return ArchiveEntry::kCompress;
+    }
+
+    bool copyFileToArchive(io::IFile* file, const std::string& outPath,
                            IArchiveWriter* writer) {
         std::unique_ptr<io::IData> data = file->openAsData();
         if (!data) {
@@ -202,7 +218,7 @@
             return false;
         }
 
-        if (writer->startEntry(outPath, flags)) {
+        if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
             if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
                                    data->size() - static_cast<size_t>(offset))) {
                 if (writer->finishEntry()) {
@@ -228,29 +244,53 @@
         return {};
     }
 
+    /**
+     * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
+     * Postcondition: ResourceTable has only one package left. All others are stripped, or there
+     *                is an error and false is returned.
+     */
     bool verifyNoExternalPackages() {
+        auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
+            return mContext.getCompilationPackage() != pkg->name ||
+                    !pkg->id ||
+                    pkg->id.value() != mContext.getPackageId();
+        };
+
         bool error = false;
         for (const auto& package : mFinalTable.packages) {
-            if (mContext.getCompilationPackage() != package->name ||
-                    !package->id || package->id.value() != mContext.getPackageId()) {
+            if (isExtPackageFunc(package)) {
                 // We have a package that is not related to the one we're building!
                 for (const auto& type : package->types) {
                     for (const auto& entry : type->entries) {
+                        ResourceNameRef resName(package->name, type->type, entry->name);
+
                         for (const auto& configValue : entry->values) {
-                            mContext.getDiagnostics()->error(
-                                    DiagMessage(configValue.value->getSource())
-                                                << "defined resource '"
-                                                << ResourceNameRef(package->name,
-                                                                   type->type,
-                                                                   entry->name)
-                                                << "' for external package '"
-                                                << package->name << "'");
-                            error = true;
+                            // Special case the occurrence of an ID that is being generated for the
+                            // 'android' package. This is due to legacy reasons.
+                            if (valueCast<Id>(configValue.value.get()) &&
+                                    package->name == u"android") {
+                                mContext.getDiagnostics()->warn(
+                                        DiagMessage(configValue.value->getSource())
+                                        << "generated id '" << resName
+                                        << "' for external package '" << package->name
+                                        << "'");
+                            } else {
+                                mContext.getDiagnostics()->error(
+                                        DiagMessage(configValue.value->getSource())
+                                        << "defined resource '" << resName
+                                        << "' for external package '" << package->name
+                                        << "'");
+                                error = true;
+                            }
                         }
                     }
                 }
             }
         }
+
+        auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
+                                         isExtPackageFunc);
+        mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
         return !error;
     }
 
@@ -295,7 +335,6 @@
             return false;
         }
 
-
         if (writer->startEntry(path, ArchiveEntry::kCompress)) {
             if (writer->writeEntry(buffer)) {
                 if (writer->finishEntry()) {
@@ -484,7 +523,10 @@
     }
 
     bool processFile(const std::string& path, bool override) {
-        if (util::stringEndsWith<char>(path, ".flata")) {
+        if (util::stringEndsWith<char>(path, ".flata") ||
+                util::stringEndsWith<char>(path, ".jar") ||
+                util::stringEndsWith<char>(path, ".jack") ||
+                util::stringEndsWith<char>(path, ".zip")) {
             return mergeArchive(path, override);
         }
 
@@ -496,7 +538,7 @@
         const Source& src = file->getSource();
         if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
             return mergeResourceTable(file, override);
-        } else {
+        } else if (util::stringEndsWith<char>(src.path, ".flat")){
             // Try opening the file and looking for an Export header.
             std::unique_ptr<io::IData> data = file->openAsData();
             if (!data) {
@@ -509,7 +551,11 @@
             if (resourceFile) {
                 return mergeCompiledFile(file, std::move(resourceFile), override);
             }
+        } else {
+            // Ignore non .flat files. This could be classes.dex or something else that happens
+            // to be in an archive.
         }
+
         return false;
     }
 
@@ -622,10 +668,7 @@
 
         bool error = false;
         {
-            ManifestFixerOptions manifestFixerOptions;
-            manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
-            manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
-            ManifestFixer manifestFixer(manifestFixerOptions);
+            ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
             if (!manifestFixer.consume(&mContext, manifestXml.get())) {
                 error = true;
             }
@@ -762,7 +805,7 @@
                     mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
                 }
 
-                if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
+                if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
                                        archiveWriter.get())) {
                     error = true;
                 }
@@ -789,12 +832,18 @@
 
         if (mOptions.generateJavaClassPath) {
             JavaClassGeneratorOptions options;
+            options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+
             if (mOptions.staticLib) {
                 options.useFinal = false;
             }
 
-            StringPiece16 actualPackage = mContext.getCompilationPackage();
+            const StringPiece16 actualPackage = mContext.getCompilationPackage();
             StringPiece16 outputPackage = mContext.getCompilationPackage();
+            if (mOptions.customJavaPackage) {
+                // Override the output java package to the custom one.
+                outputPackage = mOptions.customJavaPackage.value();
+            }
 
             if (mOptions.privateSymbols) {
                 // If we defined a private symbols package, we only emit Public symbols
@@ -802,7 +851,7 @@
 
                 options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
                 if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
-                                   mContext.getCompilationPackage(), options)) {
+                                   outputPackage, options)) {
                     return 1;
                 }
 
@@ -814,9 +863,8 @@
                 return 1;
             }
 
-            for (const std::string& extraPackage : mOptions.extraJavaPackages) {
-                if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
-                                   options)) {
+            for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
+                if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
                     return 1;
                 }
             }
@@ -853,13 +901,16 @@
     LinkOptions options;
     Maybe<std::string> privateSymbolsPackage;
     Maybe<std::string> minSdkVersion, targetSdkVersion;
+    Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
+    Maybe<std::string> versionCode, versionName;
+    Maybe<std::string> customJavaPackage;
     std::vector<std::string> extraJavaPackages;
     Flags flags = Flags()
             .requiredFlag("-o", "Output path", &options.outputPath)
             .requiredFlag("--manifest", "Path to the Android manifest to build",
                           &options.manifestPath)
             .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
-            .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. "
+            .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
                               "The last conflicting resource given takes precedence.",
                               &options.overlayFiles)
             .optionalFlag("--java", "Directory in which to generate R.java",
@@ -876,15 +927,29 @@
                           "AndroidManifest.xml", &minSdkVersion)
             .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
                           "AndroidManifest.xml", &targetSdkVersion)
+            .optionalFlag("--version-code", "Version code (integer) to inject into the "
+                          "AndroidManifest.xml if none is present", &versionCode)
+            .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
+                          "if none is present", &versionName)
             .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
             .optionalFlag("--private-symbols", "Package name to use when generating R.java for "
                           "private symbols.\n"
                           "If not specified, public and private symbols will use the application's "
                           "package name", &privateSymbolsPackage)
+            .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
+                          &customJavaPackage)
             .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
                               "package names", &extraJavaPackages)
             .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
                             "overlays without <add-resource> tags", &options.autoAddOverlay)
+            .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
+                          &renameManifestPackage)
+            .optionalFlag("--rename-instrumentation-target-package",
+                          "Changes the name of the target package for instrumentation. Most useful "
+                          "when used\nin conjunction with --rename-manifest-package",
+                          &renameInstrumentationTargetPackage)
+            .optionalFlagList("-0", "File extensions not to compress",
+                              &options.extensionsToNotCompress)
             .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
 
     if (!flags.parse("aapt2 link", args, &std::cerr)) {
@@ -896,18 +961,42 @@
     }
 
     if (minSdkVersion) {
-        options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
+        options.manifestFixerOptions.minSdkVersionDefault =
+                util::utf8ToUtf16(minSdkVersion.value());
     }
 
     if (targetSdkVersion) {
-        options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
+        options.manifestFixerOptions.targetSdkVersionDefault =
+                util::utf8ToUtf16(targetSdkVersion.value());
+    }
+
+    if (renameManifestPackage) {
+        options.manifestFixerOptions.renameManifestPackage =
+                util::utf8ToUtf16(renameManifestPackage.value());
+    }
+
+    if (renameInstrumentationTargetPackage) {
+        options.manifestFixerOptions.renameInstrumentationTargetPackage =
+                util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
+    }
+
+    if (versionCode) {
+        options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
+    }
+
+    if (versionName) {
+        options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
+    }
+
+    if (customJavaPackage) {
+        options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
     }
 
     // Populate the set of extra packages for which to generate R.java.
     for (std::string& extraPackage : extraJavaPackages) {
         // A given package can actually be a colon separated list of packages.
         for (StringPiece package : util::split(extraPackage, ':')) {
-            options.extraJavaPackages.insert(package.toString());
+            options.extraJavaPackages.insert(util::utf8ToUtf16(package));
         }
     }
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 2034c57..9baf1d8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -22,25 +22,43 @@
 namespace aapt {
 
 static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
-    bool error = false;
-
     xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
     if (!attr) {
         context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                          << "missing 'package' attribute");
-        error = true;
     } else if (ResourceUtils::isReference(attr->value)) {
         context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                          << "value for attribute 'package' must not be a "
                                             "reference");
-        error = true;
     } else if (!util::isJavaPackageName(attr->value)) {
         context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
                                          << "invalid package name '" << attr->value << "'");
-        error = true;
+    } else {
+        return true;
+    }
+    return false;
+}
+
+static bool includeVersionName(IAaptContext* context, const Source& source,
+                               const StringPiece16& versionName, xml::Element* manifestEl) {
+    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
+        return true;
     }
 
-    return !error;
+    manifestEl->attributes.push_back(xml::Attribute{
+            xml::kSchemaAndroid, u"versionName", versionName.toString() });
+    return true;
+}
+
+static bool includeVersionCode(IAaptContext* context, const Source& source,
+                               const StringPiece16& versionCode, xml::Element* manifestEl) {
+    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
+        return true;
+    }
+
+    manifestEl->attributes.push_back(xml::Attribute{
+            xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
+    return true;
 }
 
 static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
@@ -62,6 +80,76 @@
     return true;
 }
 
+class FullyQualifiedClassNameVisitor : public xml::Visitor {
+public:
+    using xml::Visitor::visit;
+
+    FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
+    }
+
+    void visit(xml::Element* el) override {
+        for (xml::Attribute& attr : el->attributes) {
+            if (Maybe<std::u16string> newValue =
+                    util::getFullyQualifiedClassName(mPackage, attr.value)) {
+                attr.value = std::move(newValue.value());
+            }
+        }
+
+        // Super implementation to iterate over the children.
+        xml::Visitor::visit(el);
+    }
+
+private:
+    StringPiece16 mPackage;
+};
+
+static bool renameManifestPackage(IAaptContext* context, const Source& source,
+                                  const StringPiece16& packageOverride, xml::Element* manifestEl) {
+    if (!util::isJavaPackageName(packageOverride)) {
+        context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
+                                         << packageOverride << "'");
+        return false;
+    }
+
+    xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
+
+    // We've already verified that the manifest element is present, with a package name specified.
+    assert(attr);
+
+    std::u16string originalPackage = std::move(attr->value);
+    attr->value = packageOverride.toString();
+
+    FullyQualifiedClassNameVisitor visitor(originalPackage);
+    manifestEl->accept(&visitor);
+    return true;
+}
+
+static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
+                                               const StringPiece16& packageOverride,
+                                               xml::Element* manifestEl) {
+    if (!util::isJavaPackageName(packageOverride)) {
+        context->getDiagnostics()->error(DiagMessage()
+                                         << "invalid instrumentation target package override '"
+                                         << packageOverride << "'");
+        return false;
+    }
+
+    xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+    if (!instrumentationEl) {
+        // No error if there is no work to be done.
+        return true;
+    }
+
+    xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+    if (!attr) {
+        // No error if there is no work to be done.
+        return true;
+    }
+
+    attr->value = packageOverride.toString();
+    return true;
+}
+
 bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
     xml::Element* root = xml::findRootElement(doc->root.get());
     if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -74,6 +162,36 @@
         return false;
     }
 
+    if (mOptions.versionCodeDefault) {
+        if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
+                                root)) {
+            return false;
+        }
+    }
+
+    if (mOptions.versionNameDefault) {
+        if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
+                                root)) {
+            return false;
+        }
+    }
+
+    if (mOptions.renameManifestPackage) {
+        // Rename manifest package.
+        if (!renameManifestPackage(context, doc->file.source,
+                                   mOptions.renameManifestPackage.value(), root)) {
+            return false;
+        }
+    }
+
+    if (mOptions.renameInstrumentationTargetPackage) {
+        if (!renameInstrumentationTargetPackage(context, doc->file.source,
+                                                mOptions.renameInstrumentationTargetPackage.value(),
+                                                root)) {
+            return false;
+        }
+    }
+
     bool foundUsesSdk = false;
     for (xml::Element* el : root->getChildElements()) {
         if (!el->namespaceUri.empty()) {
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index a77e6d5..b8d9c83 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -28,6 +28,10 @@
 struct ManifestFixerOptions {
     Maybe<std::u16string> minSdkVersionDefault;
     Maybe<std::u16string> targetSdkVersionDefault;
+    Maybe<std::u16string> renameManifestPackage;
+    Maybe<std::u16string> renameInstrumentationTargetPackage;
+    Maybe<std::u16string> versionNameDefault;
+    Maybe<std::u16string> versionCodeDefault;
 };
 
 /**
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index f6bf895..f40fbfb 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -82,8 +82,6 @@
     EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
 }
 
-
-
 TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
     ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
 
@@ -97,7 +95,7 @@
     xml::Element* el;
     xml::Attribute* attr;
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -115,7 +113,7 @@
       </manifest>)EOF", options);
     ASSERT_NE(nullptr, doc);
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -133,7 +131,7 @@
       </manifest>)EOF", options);
     ASSERT_NE(nullptr, doc);
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -149,7 +147,7 @@
                 package="android" />)EOF", options);
     ASSERT_NE(nullptr, doc);
 
-    el = xml::findRootElement(doc->root.get());
+    el = xml::findRootElement(doc.get());
     ASSERT_NE(nullptr, el);
     el = el->findChild({}, u"uses-sdk");
     ASSERT_NE(nullptr, el);
@@ -161,4 +159,98 @@
     EXPECT_EQ(u"22", attr->value);
 }
 
+TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
+    ManifestFixerOptions options;
+    options.renameManifestPackage = std::u16string(u"com.android");
+
+    std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <application name=".MainApplication" text="hello">
+          <activity name=".activity.Start" />
+          <receiver name="com.google.android.Receiver" />
+        </application>
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* manifestEl = xml::findRootElement(doc.get());
+    ASSERT_NE(nullptr, manifestEl);
+
+    xml::Attribute* attr = nullptr;
+
+    attr = manifestEl->findAttribute({}, u"package");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+
+    xml::Element* applicationEl = manifestEl->findChild({}, u"application");
+    ASSERT_NE(nullptr, applicationEl);
+
+    attr = applicationEl->findAttribute({}, u"name");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value);
+
+    attr = applicationEl->findAttribute({}, u"text");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"hello"), attr->value);
+
+    xml::Element* el;
+    el = applicationEl->findChild({}, u"activity");
+    ASSERT_NE(nullptr, el);
+
+    attr = el->findAttribute({}, u"name");
+    ASSERT_NE(nullptr, el);
+    EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value);
+
+    el = applicationEl->findChild({}, u"receiver");
+    ASSERT_NE(nullptr, el);
+
+    attr = el->findAttribute({}, u"name");
+    ASSERT_NE(nullptr, el);
+    EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
+    ManifestFixerOptions options;
+    options.renameInstrumentationTargetPackage = std::u16string(u"com.android");
+
+    std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <instrumentation android:targetPackage="android" />
+      </manifest>)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* manifestEl = xml::findRootElement(doc.get());
+    ASSERT_NE(nullptr, manifestEl);
+
+    xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+    ASSERT_NE(nullptr, instrumentationEl);
+
+    xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
+    ManifestFixerOptions options;
+    options.versionNameDefault = std::u16string(u"Beta");
+    options.versionCodeDefault = std::u16string(u"0x10000000");
+
+    std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android" />)EOF", options);
+    ASSERT_NE(nullptr, doc);
+
+    xml::Element* manifestEl = xml::findRootElement(doc.get());
+    ASSERT_NE(nullptr, manifestEl);
+
+    xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"Beta"), attr->value);
+
+    attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode");
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(std::u16string(u"0x10000000"), attr->value);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index f8e3d03..93a11b9 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -68,6 +68,12 @@
         return addValue(name, id, util::make_unique<String>(mTable->stringPool.makeRef(str)));
     }
 
+    ResourceTableBuilder& addString(const StringPiece16& name, const ResourceId id,
+                                    const ConfigDescription& config, const StringPiece16& str) {
+        return addValue(name, id, config,
+                        util::make_unique<String>(mTable->stringPool.makeRef(str)));
+    }
+
     ResourceTableBuilder& addFileReference(const StringPiece16& name, const StringPiece16& path) {
         return addFileReference(name, {}, path);
     }
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 21e476f..6b7a63cf 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -585,6 +585,13 @@
                 source.path = path.toString();
             }
             source.line = util::deviceToHost32(sourceBlock->line);
+
+            if (Style* style = valueCast<Style>(resourceValue.get())) {
+                // The parent's source is the same as the resource itself, set it here.
+                if (style->parent) {
+                    style->parent.value().setSource(source);
+                }
+            }
         }
 
         StringPiece16 comment = util::getString(mSourcePool,
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index a81dc7b..04e8199 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -163,6 +163,11 @@
     }
 
     android::FileMap fileMap;
+    if (fileStats.st_size == 0) {
+        // mmap doesn't like a length of 0. Instead we return an empty FileMap.
+        return std::move(fileMap);
+    }
+
     if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) {
         if (outError) *outError = strerror(errno);
         return {};
diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h
new file mode 100644
index 0000000..b1f9e9d
--- /dev/null
+++ b/tools/aapt2/util/ImmutableMap.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef AAPT_UTIL_IMMUTABLEMAP_H
+#define AAPT_UTIL_IMMUTABLEMAP_H
+
+#include "util/TypeTraits.h"
+
+#include <utility>
+#include <vector>
+
+namespace aapt {
+
+template <typename TKey, typename TValue>
+class ImmutableMap {
+    static_assert(is_comparable<TKey, TKey>::value, "key is not comparable");
+
+private:
+    std::vector<std::pair<TKey, TValue>> mData;
+
+    explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data) : mData(std::move(data)) {
+    }
+
+public:
+    using const_iterator = typename decltype(mData)::const_iterator;
+
+    ImmutableMap(ImmutableMap&&) = default;
+    ImmutableMap& operator=(ImmutableMap&&) = default;
+
+    ImmutableMap(const ImmutableMap&) = delete;
+    ImmutableMap& operator=(const ImmutableMap&) = delete;
+
+    static ImmutableMap<TKey, TValue> createPreSorted(
+            std::initializer_list<std::pair<TKey, TValue>> list) {
+        return ImmutableMap(std::vector<std::pair<TKey, TValue>>(list.begin(), list.end()));
+    }
+
+    static ImmutableMap<TKey, TValue> createAndSort(
+            std::initializer_list<std::pair<TKey, TValue>> list) {
+        std::vector<std::pair<TKey, TValue>> data(list.begin(), list.end());
+        std::sort(data.begin(), data.end());
+        return ImmutableMap(std::move(data));
+    }
+
+    template <typename TKey2,
+              typename = typename std::enable_if<is_comparable<TKey, TKey2>::value>::type>
+    const_iterator find(const TKey2& key) const {
+        auto cmp = [](const std::pair<TKey, TValue>& candidate, const TKey2& target) -> bool {
+            return candidate.first < target;
+        };
+
+        const_iterator endIter = end();
+        auto iter = std::lower_bound(mData.begin(), endIter, key, cmp);
+        if (iter == endIter || iter->first == key) {
+            return iter;
+        }
+        return endIter;
+    }
+
+    const_iterator begin() const {
+        return mData.begin();
+    }
+
+    const_iterator end() const {
+        return mData.end();
+    }
+};
+
+} // namespace aapt
+
+#endif /* AAPT_UTIL_IMMUTABLEMAP_H */
diff --git a/tools/aapt2/util/TypeTraits.h b/tools/aapt2/util/TypeTraits.h
new file mode 100644
index 0000000..76c13d6
--- /dev/null
+++ b/tools/aapt2/util/TypeTraits.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef AAPT_UTIL_TYPETRAITS_H
+#define AAPT_UTIL_TYPETRAITS_H
+
+#include <type_traits>
+
+namespace aapt {
+
+#define DEFINE_HAS_BINARY_OP_TRAIT(name, op) \
+    template <typename T, typename U> \
+    struct name { \
+        template <typename V, typename W> \
+        static constexpr decltype(std::declval<V>() op std::declval<W>(), bool()) test(int) { \
+        return true; \
+    } \
+    template <typename V, typename W> \
+    static constexpr bool test(...) { \
+        return false; \
+    } \
+    static constexpr bool value = test<T, U>(int()); \
+}
+
+DEFINE_HAS_BINARY_OP_TRAIT(has_eq_op, ==);
+DEFINE_HAS_BINARY_OP_TRAIT(has_lt_op, <);
+
+/**
+ * Type trait that checks if two types can be equated (==) and compared (<).
+ */
+template <typename T, typename U>
+struct is_comparable {
+    static constexpr bool value = has_eq_op<T, U>::value && has_lt_op<T, U>::value;
+};
+
+} // namespace aapt
+
+#endif /* AAPT_UTIL_TYPETRAITS_H */
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 9ecc974..7b0c71d 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -175,10 +175,11 @@
         return {};
     }
 
-    std::u16string result(package.data(), package.size());
     if (className.data()[0] != u'.') {
-        result += u'.';
+        return {};
     }
+
+    std::u16string result(package.data(), package.size());
     result.append(className.data(), className.size());
     if (!isJavaClassName(result)) {
         return {};
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 9208e07..1e0c7fa 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -144,8 +144,7 @@
 
 TEST(UtilTest, FullyQualifiedClassName) {
     Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
-    AAPT_ASSERT_TRUE(res);
-    EXPECT_EQ(res.value(), u"android.asdf");
+    AAPT_ASSERT_FALSE(res);
 
     res = util::getFullyQualifiedClassName(u"android", u".asdf");
     AAPT_ASSERT_TRUE(res);
diff --git a/tools/layoutlib/.idea/compiler.xml b/tools/layoutlib/.idea/compiler.xml
index 5aaaf18..35961a2 100644
--- a/tools/layoutlib/.idea/compiler.xml
+++ b/tools/layoutlib/.idea/compiler.xml
@@ -21,7 +21,5 @@
         <processorPath useClasspath="true" />
       </profile>
     </annotationProcessing>
-    <bytecodeTargetLevel target="1.6" />
   </component>
-</project>
-
+</project>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 31dd3d9..db4c6dc6 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -327,12 +327,19 @@
             return null;
         }
 
-        // let the framework inflate the ColorStateList from the XML file.
-        File f = new File(value);
-        if (f.isFile()) {
-            try {
-                XmlPullParser parser = ParserFactory.create(f);
 
+        try {
+            // Get the state list file content from callback to parse PSI file
+            XmlPullParser parser = mContext.getLayoutlibCallback().getXmlFileParser(value);
+            if (parser == null) {
+                // If used with a version of Android Studio that does not implement getXmlFileParser
+                // fall back to reading the file from disk
+                File f = new File(value);
+                if (f.isFile()) {
+                    parser = ParserFactory.create(f);
+                }
+            }
+            if (parser != null) {
                 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                         parser, mContext, resValue.isFramework());
                 try {
@@ -341,18 +348,18 @@
                 } finally {
                     blockParser.ensurePopped();
                 }
-            } catch (XmlPullParserException e) {
-                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to configure parser for " + value, e, null);
-                return null;
-            } catch (Exception e) {
-                // this is an error and not warning since the file existence is checked before
-                // attempting to parse it.
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                        "Failed to parse file " + value, e, null);
-
-                return null;
             }
+        } catch (XmlPullParserException e) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Failed to configure parser for " + value, e, null);
+            return null;
+        } catch (Exception e) {
+            // this is an error and not warning since the file existence is checked before
+            // attempting to parse it.
+            Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                    "Failed to parse file " + value, e, null);
+
+            return null;
         }
 
         try {
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 60514b6..8d5863b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -122,4 +122,35 @@
     /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) {
         return true;
     }
+
+    /**
+     * Set the newly decoded bitmap's density based on the Options.
+     *
+     * Copied from {@link BitmapFactory#setDensityFromOptions(Bitmap, Options)}.
+     */
+    @LayoutlibDelegate
+    /*package*/ static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
+        if (outputBitmap == null || opts == null) return;
+
+        final int density = opts.inDensity;
+        if (density != 0) {
+            outputBitmap.setDensity(density);
+            final int targetDensity = opts.inTargetDensity;
+            if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
+                return;
+            }
+
+            // --- Change from original implementation begins ---
+            // LayoutLib doesn't scale the nine patch when decoding it. Hence, don't change the
+            // density of the source bitmap in case of ninepatch.
+
+            if (opts.inScaled) {
+            // --- Change from original implementation ends. ---
+                outputBitmap.setDensity(targetDensity);
+            }
+        } else if (opts.inBitmap != null) {
+            // bitmap was reused, ensure density is reset
+            outputBitmap.setDensity(Bitmap.getDefaultDensity());
+        }
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/os/ServiceManager.java b/tools/layoutlib/bridge/src/android/os/ServiceManager.java
index 6a68ee2..549074d 100644
--- a/tools/layoutlib/bridge/src/android/os/ServiceManager.java
+++ b/tools/layoutlib/bridge/src/android/os/ServiceManager.java
@@ -51,8 +51,10 @@
 
     /**
      * Return a list of all currently running services.
+     * @return an array of all currently running services, or <code>null</code> in
+     * case of an exception
      */
-    public static String[] listServices() throws RemoteException {
+    public static String[] listServices() {
         // actual implementation returns null sometimes, so it's ok
         // to return null instead of an empty list.
         return null;
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 40437fa..9f0153a 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -19,6 +19,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 
@@ -76,7 +77,7 @@
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
             boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10,
-            Rect arg11, Configuration arg12, boolean arg13) throws RemoteException {
+            Rect arg11, Configuration arg12, boolean arg13, boolean arg14) throws RemoteException {
         // TODO Auto-generated method stub
     }
 
@@ -543,6 +544,15 @@
     }
 
     @Override
-    public void registerDockDividerVisibilityListener(IDockDividerVisibilityListener listener) {
+    public void registerDockedStackListener(IDockedStackListener listener) throws RemoteException {
+    }
+
+    @Override
+    public void setResizeDimLayer(boolean visible, int targetStackId, float alpha)
+            throws RemoteException {
+    }
+
+    @Override
+    public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java
index d691c8e..411417c 100644
--- a/tools/layoutlib/bridge/src/android/view/WindowCallback.java
+++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java
@@ -16,10 +16,13 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.view.ActionMode.Callback;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 
+import java.util.List;
+
 /**
  * An empty implementation of {@link Window.Callback} that always returns null/false.
  */
@@ -138,4 +141,9 @@
     public void onActionModeFinished(ActionMode mode) {
 
     }
+
+    @Override
+    public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, @Nullable Menu menu) {
+
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 683c4aa..c8e3d03 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -183,7 +183,7 @@
      */
     private static LayoutLog sCurrentLog = sDefaultLog;
 
-    private static final int LAST_SUPPORTED_FEATURE = Features.CHOREOGRAPHER;
+    private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR;
 
     @Override
     public int getApiLevel() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 63411b0..e6fb620 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -197,7 +197,12 @@
 
         mRenderResources = renderResources;
         mConfig = config;
-        mAssets = new BridgeAssetManager();
+        AssetManager systemAssetManager = AssetManager.getSystem();
+        if (systemAssetManager instanceof BridgeAssetManager) {
+            mAssets = (BridgeAssetManager) systemAssetManager;
+        } else {
+            throw new AssertionError("Creating BridgeContext without initializing Bridge");
+        }
         mAssets.setAssetRepository(assets);
 
         mApplicationInfo = new ApplicationInfo();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index a2fad13..08258c9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -34,7 +34,6 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
-import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageItemInfo;
@@ -98,7 +97,22 @@
     }
 
     @Override
-    public int getPackageUid(String packageName, int userHandle) throws NameNotFoundException {
+    public int[] getPackageGids(String packageName, int flags) throws NameNotFoundException {
+        return new int[0];
+    }
+
+    @Override
+    public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
+        return 0;
+    }
+
+    @Override
+    public int getPackageUidAsUser(String packageName, int userHandle) throws NameNotFoundException {
+        return 0;
+    }
+
+    @Override
+    public int getPackageUidAsUser(String packageName, int flags, int userHandle) throws NameNotFoundException {
         return 0;
     }
 
@@ -131,6 +145,12 @@
     }
 
     @Override
+    public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
+            throws NameNotFoundException {
+        return null;
+    }
+
+    @Override
     public ActivityInfo getActivityInfo(ComponentName component, int flags)
             throws NameNotFoundException {
         return null;
@@ -165,7 +185,7 @@
     }
 
     @Override
-    public List<PackageInfo> getInstalledPackages(int flags, int userId) {
+    public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
         return null;
     }
 
@@ -329,7 +349,7 @@
     }
 
     @Override
-    public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags, int userId) {
+    public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
         return null;
     }
 
@@ -522,7 +542,7 @@
     @Override
     public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
             int flags, String installerPackageName, Uri verificationURI,
-            ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
+            ContainerEncryptionParams encryptionParams) {
     }
 
     @Override
@@ -544,7 +564,7 @@
     @Override
     public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer,
             int flags, String installerPackageName, Uri verificationURI,
-            ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) {
+            ContainerEncryptionParams encryptionParams) {
     }
 
     @Override
@@ -579,12 +599,12 @@
     }
 
     @Override
-    public int getIntentVerificationStatus(String packageName, int userId) {
+    public int getIntentVerificationStatusAsUser(String packageName, int userId) {
         return 0;
     }
 
     @Override
-    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+    public boolean updateIntentVerificationStatusAsUser(String packageName, int status, int userId) {
         return false;
     }
 
@@ -599,12 +619,12 @@
     }
 
     @Override
-    public String getDefaultBrowserPackageName(int userId) {
+    public String getDefaultBrowserPackageNameAsUser(int userId) {
         return null;
     }
 
     @Override
-    public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
         return false;
     }
 
@@ -644,7 +664,7 @@
     }
 
     @Override
-    public void getPackageSizeInfo(String packageName, int userHandle,
+    public void getPackageSizeInfoAsUser(String packageName, int userHandle,
             IPackageStatsObserver observer) {
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 3662573..48012db 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -16,6 +16,8 @@
 
 package com.android.layoutlib.bridge.android;
 
+import com.android.internal.os.IResultReceiver;
+
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -95,6 +97,10 @@
     }
 
     @Override
+    public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
+    }
+
+    @Override
     public IBinder asBinder() {
         // pass for now.
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 5c73fb6a..aae5ac4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -89,7 +89,8 @@
     @Override
     public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
             int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
-            Rect rect6, Configuration configuration, Surface surface) throws RemoteException {
+            Rect rect6, Rect rect7, Configuration configuration, Surface surface)
+            throws RemoteException {
         // pass for now.
         return 0;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 7e5ae8d..d417eb7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -63,4 +63,9 @@
     public void removeViewImmediate(View arg0) {
         // pass
     }
+
+    @Override
+    public void requestAppKeyboardShortcuts(
+            KeyboardShortcutsReceiver receiver) {
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 9c89bfe..dfbc69b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -19,9 +19,6 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.Density;
 
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -41,29 +38,18 @@
     private static final int WIDTH_DEFAULT = 36;
     private static final int WIDTH_SW360 = 40;
     private static final int WIDTH_SW600 = 48;
-    private static final String LAYOUT_XML = "/bars/navigation_bar.xml";
+    protected static final String LAYOUT_XML = "/bars/navigation_bar.xml";
     private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml";
 
-
-    /**
-     * Constructor to be used when creating the {@link NavigationBar} as a regular control.
-     * This is currently used by the theme editor.
-     */
-    @SuppressWarnings("unused")
-    public NavigationBar(Context context, AttributeSet attrs) {
-        this((BridgeContext) context,
-                Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
-                LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
-                ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
-                        View.LAYOUT_DIRECTION_RTL,
-                (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
-                0);
+    public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
+      boolean rtlEnabled, int simulatedPlatformVersion) {
+        this(context, density, orientation, isRtl, rtlEnabled, simulatedPlatformVersion,
+          getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML);
     }
 
-    public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
-            boolean rtlEnabled, int simulatedPlatformVersion) {
-        super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
-                "navigation_bar.xml", simulatedPlatformVersion);
+    protected NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
+      boolean rtlEnabled, int simulatedPlatformVersion, String layoutPath) {
+        super(context, orientation, layoutPath, "navigation_bar.xml", simulatedPlatformVersion);
 
         int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
         setBackgroundColor(color == 0 ? 0xFF000000 : color);
@@ -117,7 +103,7 @@
         view.setLayoutParams(layoutParams);
     }
 
-    private static int getSidePadding(float sw) {
+    protected int getSidePadding(float sw) {
         if (sw >= 400) {
             return PADDING_WIDTH_SW400;
         }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
new file mode 100644
index 0000000..0435280
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.layoutlib.bridge.bars;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.resources.Density;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Navigation Bar for the Theme Editor preview.
+ *
+ * For small bars, it is identical to {@link NavigationBar}.
+ * But wide bars from {@link NavigationBar} are too wide for the Theme Editor preview.
+ * To solve that problem, {@link ThemePreviewNavigationBar} use the layout for small bars,
+ * and have no padding on the sides. That way, they have a similar look as the true ones,
+ * and they fit in the Theme Editor preview.
+ */
+public class ThemePreviewNavigationBar extends NavigationBar {
+    private static final int PADDING_WIDTH_SW600 = 0;
+
+    @SuppressWarnings("unused")
+    public ThemePreviewNavigationBar(Context context, AttributeSet attrs) {
+        super((BridgeContext) context,
+                Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
+                LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
+                ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
+                        View.LAYOUT_DIRECTION_RTL,
+                (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
+                0, LAYOUT_XML);
+    }
+
+    @Override
+    protected int getSidePadding(float sw) {
+        if (sw >= 600) {
+            return PADDING_WIDTH_SW600;
+        }
+        return super.getSidePadding(sw);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
index 9aab340..8c60bae 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -114,7 +114,7 @@
                         if (value != null) {
                             if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) {
                                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
-                                        "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
+                                        "Wrong Adapter Item value class for IS_CHECKED. Expected Boolean, got %s",
                                         value.getClass().getName()), null);
                             } else {
                                 cb.setChecked((Boolean) value);
@@ -134,7 +134,7 @@
                         if (value != null) {
                             if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) {
                                 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
-                                        "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
+                                        "Wrong Adapter Item value class for SRC. Expected Boolean, got %s",
                                         value.getClass().getName()), null);
                             } else {
                                 // FIXME
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index 2b86bfb..d8ead23 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
new file mode 100644
index 0000000..65d1dc5
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 2dca07c..b1b3759 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -305,6 +305,11 @@
         renderAndVerify("array_check.xml", "array_check.png");
     }
 
+    @Test
+    public void testAllWidgetsTablet() throws ClassNotFoundException {
+        renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
+    }
+
     @AfterClass
     public static void tearDown() {
         sLayoutLibLog = null;
@@ -375,10 +380,11 @@
     }
 
     /**
-     * Create a new rendering session and test that rendering given layout on nexus 5
-     * doesn't throw any exceptions and matches the provided image.
-     * <p/>If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time
-     * indicates how far in the future is.
+     * Create a new rendering session and test that rendering the given layout doesn't throw any
+     * exceptions and matches the provided image.
+     * <p>
+     * If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time indicates
+     * how far in the future is.
      */
     private void renderAndVerify(SessionParams params, String goldenFileName, long frameTimeNanos)
             throws ClassNotFoundException {
@@ -409,8 +415,8 @@
     }
 
     /**
-     * Create a new rendering session and test that rendering given layout on nexus 5
-     * doesn't throw any exceptions and matches the provided image.
+     * Create a new rendering session and test that rendering the given layout doesn't throw any
+     * exceptions and matches the provided image.
      */
     private void renderAndVerify(SessionParams params, String goldenFileName)
             throws ClassNotFoundException {
@@ -418,11 +424,21 @@
     }
 
     /**
-     * Create a new rendering session and test that rendering given layout on nexus 5
+     * Create a new rendering session and test that rendering the given layout on nexus 5
      * doesn't throw any exceptions and matches the provided image.
      */
     private void renderAndVerify(String layoutFileName, String goldenFileName)
             throws ClassNotFoundException {
+        renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5);
+    }
+
+    /**
+     * Create a new rendering session and test that rendering the given layout on given device
+     * doesn't throw any exceptions and matches the provided image.
+     */
+    private void renderAndVerify(String layoutFileName, String goldenFileName,
+            ConfigGenerator deviceConfig)
+            throws ClassNotFoundException {
         // Create the layout pull parser.
         LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
         // Create LayoutLibCallback.
@@ -430,7 +446,7 @@
         layoutLibCallback.initResources();
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+        SessionParams params = getSessionParams(parser, deviceConfig,
                 layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
         renderAndVerify(params, goldenFileName);
     }
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
index 8e0cec6..34fc726 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
@@ -126,6 +126,21 @@
                                                         .setSoftButtons(true)
                                                         .setNavigation(Navigation.NONAV);
 
+    public static final ConfigGenerator NEXUS_7_2012 = new ConfigGenerator()
+                                                        .setScreenHeight(1280)
+                                                        .setScreenWidth(800)
+                                                        .setXdpi(195)
+                                                        .setYdpi(200)
+                                                        .setOrientation(ScreenOrientation.PORTRAIT)
+                                                        .setDensity(Density.TV)
+                                                        .setRatio(ScreenRatio.NOTLONG)
+                                                        .setSize(ScreenSize.LARGE)
+                                                        .setKeyboard(Keyboard.NOKEY)
+                                                        .setTouchScreen(TouchScreen.FINGER)
+                                                        .setKeyboardState(KeyboardState.SOFT)
+                                                        .setSoftButtons(true)
+                                                        .setNavigation(Navigation.NONAV);
+
     private static final String TAG_ATTR = "attr";
     private static final String TAG_ENUM = "enum";
     private static final String TAG_FLAG = "flag";
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
index e6f0bc3..c7f2c41 100644
--- a/tools/layoutlib/create/Android.mk
+++ b/tools/layoutlib/create/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_JAR_MANIFEST := manifest.txt
 LOCAL_STATIC_JAVA_LIBRARIES := \
-	asm-4.0
+	asm-5.0
 
 LOCAL_MODULE := layoutlib_create
 
diff --git a/tools/layoutlib/create/create.iml b/tools/layoutlib/create/create.iml
index 9b18e73..b2b14b4 100644
--- a/tools/layoutlib/create/create.iml
+++ b/tools/layoutlib/create/create.iml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
@@ -9,12 +9,12 @@
       <sourceFolder url="file://$MODULE_DIR$/tests/mock_data" type="java-test-resource" />
       <excludeFolder url="file://$MODULE_DIR$/.settings" />
     </content>
-    <orderEntry type="inheritedJdk" />
+    <orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="module-library">
-      <library name="asm-4.0">
+      <library name="asm-5.0">
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/asm/asm-4.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../../prebuilts/misc/common/asm/asm-5.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES>
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
index a6902a4..758bd48 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
@@ -21,7 +21,6 @@
 import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.signature.SignatureReader;
 import org.objectweb.asm.signature.SignatureVisitor;
@@ -44,7 +43,7 @@
     abstract String renameInternalType(String name);
 
     public AbstractClassAdapter(ClassVisitor cv) {
-        super(Opcodes.ASM4, cv);
+        super(Main.ASM_VERSION, cv);
     }
 
     /**
@@ -239,7 +238,7 @@
          * The names must be full qualified internal ASM names (e.g. com/blah/MyClass$InnerClass).
          */
         public RenameMethodAdapter(MethodVisitor mv) {
-            super(Opcodes.ASM4, mv);
+            super(Main.ASM_VERSION, mv);
         }
 
         @Override
@@ -276,7 +275,8 @@
         }
 
         @Override
-        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        public void visitMethodInsn(int opcode, String owner, String name, String desc,
+                boolean itf) {
             // The owner sometimes turns out to be a type descriptor. We try to detect it and fix.
             if (owner.indexOf(';') > 0) {
                 owner = renameTypeDesc(owner);
@@ -285,7 +285,7 @@
             }
             desc = renameMethodDesc(desc);
 
-            super.visitMethodInsn(opcode, owner, name, desc);
+            super.visitMethodInsn(opcode, owner, name, desc, itf);
         }
 
         @Override
@@ -330,7 +330,7 @@
         private final SignatureVisitor mSv;
 
         public RenameSignatureAdapter(SignatureVisitor sv) {
-            super(Opcodes.ASM4);
+            super(Main.ASM_VERSION);
             mSv = sv;
         }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index c8b2b84..48544ca 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -23,7 +23,6 @@
 import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.signature.SignatureReader;
 import org.objectweb.asm.signature.SignatureVisitor;
@@ -65,7 +64,7 @@
     /** Glob patterns of files to keep as is. */
     private final String[] mIncludeFileGlobs;
     /** Internal names of classes that contain method calls that need to be rewritten. */
-    private final Set<String> mReplaceMethodCallClasses = new HashSet<String>();
+    private final Set<String> mReplaceMethodCallClasses = new HashSet<>();
 
     /**
      * Creates a new analyzer.
@@ -97,8 +96,8 @@
      */
     public void analyze() throws IOException, LogAbortException {
 
-        TreeMap<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        TreeMap<String, ClassReader> zipClasses = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
 
         parseZip(mOsSourceJar, zipClasses, filesFound);
         mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
@@ -189,7 +188,7 @@
      */
     Map<String, ClassReader> findIncludes(Map<String, ClassReader> zipClasses)
             throws LogAbortException {
-        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> found = new TreeMap<>();
 
         mLog.debug("Find classes to include.");
 
@@ -318,10 +317,10 @@
     Map<String, ClassReader> findDeps(Map<String, ClassReader> zipClasses,
             Map<String, ClassReader> inOutKeepClasses) {
 
-        TreeMap<String, ClassReader> deps = new TreeMap<String, ClassReader>();
-        TreeMap<String, ClassReader> new_deps = new TreeMap<String, ClassReader>();
-        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
-        TreeMap<String, ClassReader> temp = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> deps = new TreeMap<>();
+        TreeMap<String, ClassReader> new_deps = new TreeMap<>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<>();
+        TreeMap<String, ClassReader> temp = new TreeMap<>();
 
         DependencyVisitor visitor = getVisitor(zipClasses,
                 inOutKeepClasses, new_keep,
@@ -399,7 +398,7 @@
                 Map<String, ClassReader> outKeep,
                 Map<String,ClassReader> inDeps,
                 Map<String,ClassReader> outDeps) {
-            super(Opcodes.ASM4);
+            super(Main.ASM_VERSION);
             mZipClasses = zipClasses;
             mInKeep = inKeep;
             mOutKeep = outKeep;
@@ -557,7 +556,7 @@
         private class MyFieldVisitor extends FieldVisitor {
 
             public MyFieldVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
             @Override
@@ -630,7 +629,7 @@
             private String mOwnerClass;
 
             public MyMethodVisitor(String ownerClass) {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
                 mOwnerClass = ownerClass;
             }
 
@@ -719,7 +718,8 @@
 
             // instruction that invokes a method
             @Override
-            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            public void visitMethodInsn(int opcode, String owner, String name, String desc,
+                    boolean itf) {
 
                 // owner is the internal name of the method's owner class
                 considerName(owner);
@@ -779,7 +779,7 @@
         private class MySignatureVisitor extends SignatureVisitor {
 
             public MySignatureVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
             // ---------------------------------------------------
@@ -878,7 +878,7 @@
         private class MyAnnotationVisitor extends AnnotationVisitor {
 
             public MyAnnotationVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
             // Visits a primitive value of an annotation
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 8f0ad01..5b99a6b 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -91,7 +91,7 @@
         mLog = log;
         mOsDestJar = osDestJar;
         ArrayList<Class<?>> injectedClasses =
-                new ArrayList<Class<?>>(Arrays.asList(createInfo.getInjectedClasses()));
+                new ArrayList<>(Arrays.asList(createInfo.getInjectedClasses()));
         // Search for and add anonymous inner classes also.
         ListIterator<Class<?>> iter = injectedClasses.listIterator();
         while (iter.hasNext()) {
@@ -107,25 +107,25 @@
             }
         }
         mInjectClasses = injectedClasses.toArray(new Class<?>[0]);
-        mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods()));
+        mStubMethods = new HashSet<>(Arrays.asList(createInfo.getOverriddenMethods()));
 
         // Create the map/set of methods to change to delegates
-        mDelegateMethods = new HashMap<String, Set<String>>();
+        mDelegateMethods = new HashMap<>();
         addToMap(createInfo.getDelegateMethods(), mDelegateMethods);
 
         for (String className : createInfo.getDelegateClassNatives()) {
             className = binaryToInternalClassName(className);
             Set<String> methods = mDelegateMethods.get(className);
             if (methods == null) {
-                methods = new HashSet<String>();
+                methods = new HashSet<>();
                 mDelegateMethods.put(className, methods);
             }
             methods.add(DelegateClassAdapter.ALL_NATIVES);
         }
 
         // Create the map of classes to rename.
-        mRenameClasses = new HashMap<String, String>();
-        mClassesNotRenamed = new HashSet<String>();
+        mRenameClasses = new HashMap<>();
+        mClassesNotRenamed = new HashSet<>();
         String[] renameClasses = createInfo.getRenamedClasses();
         int n = renameClasses.length;
         for (int i = 0; i < n; i += 2) {
@@ -138,7 +138,7 @@
         }
 
         // Create a map of classes to be refactored.
-        mRefactorClasses = new HashMap<String, String>();
+        mRefactorClasses = new HashMap<>();
         String[] refactorClasses = createInfo.getJavaPkgClasses();
         n = refactorClasses.length;
         for (int i = 0; i < n; i += 2) {
@@ -149,7 +149,7 @@
         }
 
         // create the map of renamed class -> return type of method to delete.
-        mDeleteReturns = new HashMap<String, Set<String>>();
+        mDeleteReturns = new HashMap<>();
         String[] deleteReturns = createInfo.getDeleteReturns();
         Set<String> returnTypes = null;
         String renamedClass = null;
@@ -172,12 +172,12 @@
 
             // just a standard return type, we add it to the list.
             if (returnTypes == null) {
-                returnTypes = new HashSet<String>();
+                returnTypes = new HashSet<>();
             }
             returnTypes.add(binaryToInternalClassName(className));
         }
 
-        mPromotedFields = new HashMap<String, Set<String>>();
+        mPromotedFields = new HashMap<>();
         addToMap(createInfo.getPromotedFields(), mPromotedFields);
 
         mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
@@ -197,7 +197,7 @@
             String methodOrFieldName = entry.substring(pos + 1);
             Set<String> set = map.get(className);
             if (set == null) {
-                set = new HashSet<String>();
+                set = new HashSet<>();
                 map.put(className, set);
             }
             set.add(methodOrFieldName);
@@ -247,7 +247,7 @@
 
     /** Generates the final JAR */
     public void generate() throws IOException {
-        TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
+        TreeMap<String, byte[]> all = new TreeMap<>();
 
         for (Class<?> clazz : mInjectClasses) {
             String name = classToEntryPath(clazz);
@@ -314,7 +314,7 @@
      * e.g. for the input "android.view.View" it returns "android/view/View.class"
      */
     String classNameToEntryPath(String className) {
-        return className.replaceAll("\\.", "/").concat(".class");
+        return className.replace('.', '/').concat(".class");
     }
 
     /**
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
index 2c955fd..4748a7c 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java
@@ -31,7 +31,7 @@
  */
 public class ClassHasNativeVisitor extends ClassVisitor {
     public ClassHasNativeVisitor() {
-        super(Opcodes.ASM4);
+        super(Main.ASM_VERSION);
     }
 
     private boolean mHasNativeMethods = false;
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index b571c5a..6e6ad8f 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -111,7 +111,7 @@
     public Set<String> getExcludedClasses() {
         String[] refactoredClasses = getJavaPkgClasses();
         int count = refactoredClasses.length / 2 + EXCLUDED_CLASSES.length;
-        Set<String> excludedClasses = new HashSet<String>(count);
+        Set<String> excludedClasses = new HashSet<>(count);
         for (int i = 0; i < refactoredClasses.length; i+=2) {
             excludedClasses.add(refactoredClasses[i]);
         }
@@ -166,6 +166,7 @@
         "android.content.res.TypedArray#getValueAt",
         "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
+        "android.graphics.BitmapFactory#setDensityFromOptions",
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index 7ef7566..cbb3a8a 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -60,7 +60,7 @@
             ClassVisitor cv,
             String className,
             Set<String> delegateMethods) {
-        super(Opcodes.ASM4, cv);
+        super(Main.ASM_VERSION, cv);
         mLog = log;
         mClassName = className;
         mDelegateMethods = delegateMethods;
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index cca9e57..da8babc 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -124,7 +124,7 @@
             String desc,
             boolean isStatic,
             boolean isStaticClass) {
-        super(Opcodes.ASM4);
+        super(Main.ASM_VERSION);
         mLog = log;
         mOrgWriter = mvOriginal;
         mDelWriter = mvDelegate;
@@ -188,7 +188,7 @@
             mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
         }
 
-        ArrayList<Type> paramTypes = new ArrayList<Type>();
+        ArrayList<Type> paramTypes = new ArrayList<>();
         String delegateClassName = mClassName + DELEGATE_SUFFIX;
         boolean pushedArg0 = false;
         int maxStack = 0;
@@ -253,7 +253,8 @@
         mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC,
                 delegateClassName,
                 mMethodName,
-                desc);
+                desc,
+                false);
 
         Type returnType = Type.getReturnType(mDesc);
         mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
@@ -371,9 +372,9 @@
     }
 
     @Override
-    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
         if (mOrgWriter != null) {
-            mOrgWriter.visitMethodInsn(opcode, owner, name, desc);
+            mOrgWriter.visitMethodInsn(opcode, owner, name, desc, itf);
         }
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
index 61b64a2..aa68ea0 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
@@ -26,7 +26,6 @@
 import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.signature.SignatureReader;
 import org.objectweb.asm.signature.SignatureVisitor;
@@ -82,7 +81,7 @@
 
         Map<String, Set<String>> missing = findMissingClasses(deps, zipClasses.keySet());
 
-        List<Map<String, Set<String>>> result = new ArrayList<Map<String,Set<String>>>(2);
+        List<Map<String, Set<String>>> result = new ArrayList<>(2);
         result.add(deps);
         result.add(missing);
         return result;
@@ -151,7 +150,7 @@
      * class name => ASM ClassReader. Class names are in the form "android.view.View".
      */
     Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
-        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> classes = new TreeMap<>();
 
         for (String jarPath : jarPathList) {
             ZipFile zip = new ZipFile(jarPath);
@@ -202,7 +201,7 @@
 
         // The dependencies that we'll collect.
         // It's a map Class name => uses class names.
-        Map<String, Set<String>> dependencyMap = new TreeMap<String, Set<String>>();
+        Map<String, Set<String>> dependencyMap = new TreeMap<>();
 
         DependencyVisitor visitor = getVisitor();
 
@@ -211,7 +210,7 @@
             for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
                 String name = entry.getKey();
 
-                TreeSet<String> set = new TreeSet<String>();
+                TreeSet<String> set = new TreeSet<>();
                 dependencyMap.put(name, set);
                 visitor.setDependencySet(set);
 
@@ -240,7 +239,7 @@
     private Map<String, Set<String>> findMissingClasses(
             Map<String, Set<String>> deps,
             Set<String> zipClasses) {
-        Map<String, Set<String>> missing = new TreeMap<String, Set<String>>();
+        Map<String, Set<String>> missing = new TreeMap<>();
 
         for (Entry<String, Set<String>> entry : deps.entrySet()) {
             String name = entry.getKey();
@@ -250,7 +249,7 @@
                     // This dependency doesn't exist in the zip classes.
                     Set<String> set = missing.get(dep);
                     if (set == null) {
-                        set = new TreeSet<String>();
+                        set = new TreeSet<>();
                         missing.put(dep, set);
                     }
                     set.add(name);
@@ -284,7 +283,7 @@
          * Creates a new visitor that will find all the dependencies for the visited class.
          */
         public DependencyVisitor() {
-            super(Opcodes.ASM4);
+            super(Main.ASM_VERSION);
         }
 
         /**
@@ -435,7 +434,7 @@
         private class MyFieldVisitor extends FieldVisitor {
 
             public MyFieldVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
             @Override
@@ -510,7 +509,7 @@
         private class MyMethodVisitor extends MethodVisitor {
 
             public MyMethodVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
 
@@ -598,7 +597,8 @@
 
             // instruction that invokes a method
             @Override
-            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            public void visitMethodInsn(int opcode, String owner, String name, String desc,
+                    boolean itf) {
 
                 // owner is the internal name of the method's owner class
                 if (!considerDesc(owner) && owner.indexOf('/') != -1) {
@@ -654,7 +654,7 @@
         private class MySignatureVisitor extends SignatureVisitor {
 
             public MySignatureVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
             // ---------------------------------------------------
@@ -753,7 +753,7 @@
         private class MyAnnotationVisitor extends AnnotationVisitor {
 
             public MyAnnotationVisitor() {
-                super(Opcodes.ASM4);
+                super(Main.ASM_VERSION);
             }
 
             // Visits a primitive value of an annotation
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java
index 37fc096..1941ab4 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodRunnables.java
@@ -42,9 +42,9 @@
             mv.visitCode();
             mv.visitVarInsn(ALOAD, 0);
             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass",
-                    "()Ljava/lang/Class;");
+                    "()Ljava/lang/Class;", false);
             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader",
-                    "()Ljava/lang/ClassLoader;");
+                    "()Ljava/lang/ClassLoader;", false);
             mv.visitInsn(ARETURN);
             mv.visitMaxs(1, 1);
             mv.visitEnd();
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java
index ea2b9c9..c834808 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/InjectMethodsAdapter.java
@@ -19,7 +19,6 @@
 import com.android.tools.layoutlib.create.ICreateInfo.InjectMethodRunnable;
 
 import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.Opcodes;
 
 /**
  * Injects methods into some classes.
@@ -29,7 +28,7 @@
     private final ICreateInfo.InjectMethodRunnable mRunnable;
 
     public InjectMethodsAdapter(ClassVisitor cv, InjectMethodRunnable runnable) {
-        super(Opcodes.ASM4, cv);
+        super(Main.ASM_VERSION, cv);
         mRunnable = runnable;
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index 383168f..9bb91e5 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -16,6 +16,8 @@
 
 package com.android.tools.layoutlib.create;
 
+import org.objectweb.asm.Opcodes;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -52,13 +54,15 @@
         public boolean listOnlyMissingDeps = false;
     }
 
+    public static final int ASM_VERSION = Opcodes.ASM5;
+
     public static final Options sOptions = new Options();
 
     public static void main(String[] args) {
 
         Log log = new Log();
 
-        ArrayList<String> osJarPath = new ArrayList<String>();
+        ArrayList<String> osJarPath = new ArrayList<>();
         String[] osDestJar = { null };
 
         if (!processArgs(log, args, osJarPath, osDestJar)) {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
index 6fc2b24..faba4d7 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/MethodListener.java
@@ -36,41 +36,40 @@
      * @param isNative True if the method was a native method.
      * @param caller The calling object. Null for static methods, "this" for instance methods.
      */
-    public void onInvokeV(String signature, boolean isNative, Object caller);
+    void onInvokeV(String signature, boolean isNative, Object caller);
 
     /**
      * Same as {@link #onInvokeV(String, boolean, Object)} but returns an integer or similar.
      * @see #onInvokeV(String, boolean, Object)
      * @return an integer, or a boolean, or a short or a byte.
      */
-    public int onInvokeI(String signature, boolean isNative, Object caller);
+    int onInvokeI(String signature, boolean isNative, Object caller);
 
     /**
      * Same as {@link #onInvokeV(String, boolean, Object)} but returns a long.
      * @see #onInvokeV(String, boolean, Object)
      * @return a long.
      */
-    public long onInvokeL(String signature, boolean isNative, Object caller);
+    long onInvokeL(String signature, boolean isNative, Object caller);
 
     /**
      * Same as {@link #onInvokeV(String, boolean, Object)} but returns a float.
      * @see #onInvokeV(String, boolean, Object)
      * @return a float.
      */
-    public float onInvokeF(String signature, boolean isNative, Object caller);
+    float onInvokeF(String signature, boolean isNative, Object caller);
 
     /**
      * Same as {@link #onInvokeV(String, boolean, Object)} but returns a double.
      * @see #onInvokeV(String, boolean, Object)
      * @return a double.
      */
-    public double onInvokeD(String signature, boolean isNative, Object caller);
+    double onInvokeD(String signature, boolean isNative, Object caller);
 
     /**
      * Same as {@link #onInvokeV(String, boolean, Object)} but returns an object.
      * @see #onInvokeV(String, boolean, Object)
      * @return an object.
      */
-    public Object onInvokeA(String signature, boolean isNative, Object caller);
+    Object onInvokeA(String signature, boolean isNative, Object caller);
 }
-    
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
index 4c87b3c..7ccafc3 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
@@ -28,7 +28,7 @@
 public final class OverrideMethod {
 
     /** Map of method overridden. */
-    private static HashMap<String, MethodListener> sMethods = new HashMap<String, MethodListener>();
+    private static HashMap<String, MethodListener> sMethods = new HashMap<>();
     /** Default listener for all method not listed in sMethods. Nothing if null. */
     private static MethodListener sDefaultListener = null;
     
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java
index e4b70da..05af033 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java
@@ -24,7 +24,6 @@
 import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
 import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
-import static org.objectweb.asm.Opcodes.ASM4;
 
 /**
  * Promotes given fields to public visibility.
@@ -35,7 +34,7 @@
     private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED);
 
     public PromoteFieldClassAdapter(ClassVisitor cv, Set<String> fieldNames) {
-        super(ASM4, cv);
+        super(Main.ASM_VERSION, cv);
         mFieldNames = fieldNames;
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 5e47261..bf94415 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -43,11 +43,11 @@
      * Descriptors for specialized versions {@link System#arraycopy} that are not present on the
      * Desktop VM.
      */
-    private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<String>(Arrays.asList(
+    private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList(
             "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
             "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
 
-    private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<MethodReplacer>(5);
+    private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<>(5);
 
     private static final String ANDROID_LOCALE_CLASS =
             "com/android/layoutlib/bridge/android/AndroidLocale";
@@ -232,7 +232,7 @@
     private final String mOriginalClassName;
 
     public ReplaceMethodCallsAdapter(ClassVisitor cv, String originalClassName) {
-        super(Opcodes.ASM4, cv);
+        super(Main.ASM_VERSION, cv);
         mOriginalClassName = originalClassName;
     }
 
@@ -245,11 +245,12 @@
     private class MyMethodVisitor extends MethodVisitor {
 
         public MyMethodVisitor(MethodVisitor mv) {
-            super(Opcodes.ASM4, mv);
+            super(Main.ASM_VERSION, mv);
         }
 
         @Override
-        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        public void visitMethodInsn(int opcode, String owner, String name, String desc,
+                boolean itf) {
             for (MethodReplacer replacer : METHOD_REPLACERS) {
                 if (replacer.isNeeded(owner, name, desc, mOriginalClassName)) {
                     MethodInformation mi = new MethodInformation(opcode, owner, name, desc);
@@ -261,7 +262,7 @@
                     break;
                 }
             }
-            super.visitMethodInsn(opcode, owner, name, desc);
+            super.visitMethodInsn(opcode, owner, name, desc, itf);
         }
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
index 416b73a..b5ab738 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
@@ -50,7 +50,7 @@
 
     public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
             String invokeSignature, boolean isStatic, boolean isNative) {
-        super(Opcodes.ASM4);
+        super(Main.ASM_VERSION);
         mParentVisitor = mv;
         mReturnType = returnType;
         mInvokeSignature = invokeSignature;
@@ -82,7 +82,8 @@
             mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                     "com/android/tools/layoutlib/create/OverrideMethod",
                     "invokeV",
-                    "(Ljava/lang/String;ZLjava/lang/Object;)V");
+                    "(Ljava/lang/String;ZLjava/lang/Object;)V",
+                    false);
             mParentVisitor.visitInsn(Opcodes.RETURN);
             break;
         case Type.BOOLEAN:
@@ -93,7 +94,8 @@
             mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                     "com/android/tools/layoutlib/create/OverrideMethod",
                     "invokeI",
-                    "(Ljava/lang/String;ZLjava/lang/Object;)I");
+                    "(Ljava/lang/String;ZLjava/lang/Object;)I",
+                    false);
             switch(sort) {
             case Type.BOOLEAN:
                 Label l1 = new Label();
@@ -119,21 +121,24 @@
             mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                     "com/android/tools/layoutlib/create/OverrideMethod",
                     "invokeL",
-                    "(Ljava/lang/String;ZLjava/lang/Object;)J");
+                    "(Ljava/lang/String;ZLjava/lang/Object;)J",
+                    false);
             mParentVisitor.visitInsn(Opcodes.LRETURN);
             break;
         case Type.FLOAT:
             mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                     "com/android/tools/layoutlib/create/OverrideMethod",
                     "invokeF",
-                    "(Ljava/lang/String;ZLjava/lang/Object;)F");
+                    "(Ljava/lang/String;ZLjava/lang/Object;)F",
+                    false);
             mParentVisitor.visitInsn(Opcodes.FRETURN);
             break;
         case Type.DOUBLE:
             mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                     "com/android/tools/layoutlib/create/OverrideMethod",
                     "invokeD",
-                    "(Ljava/lang/String;ZLjava/lang/Object;)D");
+                    "(Ljava/lang/String;ZLjava/lang/Object;)D",
+                    false);
             mParentVisitor.visitInsn(Opcodes.DRETURN);
             break;
         case Type.ARRAY:
@@ -141,7 +146,8 @@
             mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                     "com/android/tools/layoutlib/create/OverrideMethod",
                     "invokeA",
-                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
+                    "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;",
+                    false);
             mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
             mParentVisitor.visitInsn(Opcodes.ARETURN);
             break;
@@ -282,9 +288,9 @@
     }
 
     @Override
-    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
         if (mIsInitMethod) {
-            mParentVisitor.visitMethodInsn(opcode, owner, name, desc);
+            mParentVisitor.visitMethodInsn(opcode, owner, name, desc, itf);
         }
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
index d9ecf98..a28ae69 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -49,7 +49,7 @@
     public TransformClassAdapter(Log logger, Set<String> stubMethods,
             Set<String> deleteReturns, String className, ClassVisitor cv,
             boolean stubNativesOnly) {
-        super(Opcodes.ASM4, cv);
+        super(Main.ASM_VERSION, cv);
         mLog = logger;
         mStubMethods = stubMethods;
         mClassName = className;
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java
index ed2c128..7d6c4ec 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/AutoCloseable.java
@@ -28,4 +28,5 @@
     /**
      * Closes the object and release any system resources it holds.
      */
-    void close() throws Exception; }
+    void close() throws Exception;
+}
diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk
index c197d57..dafb9c6 100644
--- a/tools/layoutlib/create/tests/Android.mk
+++ b/tools/layoutlib/create/tests/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_JAVA_LIBRARIES := layoutlib_create junit
-LOCAL_STATIC_JAVA_LIBRARIES := asm-4.0
+LOCAL_STATIC_JAVA_LIBRARIES := asm-5.0
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 78e2c48..f86917a 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -17,13 +17,8 @@
 
 package com.android.tools.layoutlib.create;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
 import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.objectweb.asm.ClassReader;
@@ -32,11 +27,15 @@
 import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
 /**
  * Unit tests for some methods of {@link AsmAnalyzer}.
  */
@@ -51,26 +50,22 @@
         mLog = new MockLog();
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
 
-        mOsJarPath = new ArrayList<String>();
+        mOsJarPath = new ArrayList<>();
+        //noinspection ConstantConditions
         mOsJarPath.add(url.getFile());
 
-        Set<String> excludeClasses = new HashSet<String>(1);
-        excludeClasses.add("java.lang.JavaClass");
+        Set<String> excludeClasses = Collections.singleton("java.lang.JavaClass");
 
         String[] includeFiles = new String[]{"mock_android/data/data*"};
         mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */, null /* deriveFrom */,
                 null /* includeGlobs */, excludeClasses, includeFiles);
     }
 
-    @After
-    public void tearDown() throws Exception {
-    }
-
     @Test
     public void testParseZip() throws IOException {
 
-        Map<String, ClassReader> map = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> map = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
 
         mAa.parseZip(mOsJarPath, map, filesFound);
 
@@ -101,11 +96,11 @@
     @Test
     public void testFindClass() throws IOException, LogAbortException {
 
-        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> zipClasses = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
 
         mAa.parseZip(mOsJarPath, zipClasses, filesFound);
-        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> found = new TreeMap<>();
 
         ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
                 zipClasses, found);
@@ -120,11 +115,11 @@
     @Test
     public void testFindGlobs() throws IOException, LogAbortException {
 
-        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> zipClasses = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
 
         mAa.parseZip(mOsJarPath, zipClasses, filesFound);
-        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> found = new TreeMap<>();
 
         // this matches classes, a package match returns nothing
         found.clear();
@@ -183,11 +178,11 @@
     @Test
     public void testFindClassesDerivingFrom() throws LogAbortException, IOException {
 
-        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> zipClasses = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
 
         mAa.parseZip(mOsJarPath, zipClasses, filesFound);
-        TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> found = new TreeMap<>();
 
         mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
 
@@ -209,14 +204,14 @@
     @Test
     public void testDependencyVisitor() throws IOException, LogAbortException {
 
-        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> zipClasses = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
 
         mAa.parseZip(mOsJarPath, zipClasses, filesFound);
-        TreeMap<String, ClassReader> keep = new TreeMap<String, ClassReader>();
-        TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
-        TreeMap<String, ClassReader> in_deps = new TreeMap<String, ClassReader>();
-        TreeMap<String, ClassReader> out_deps = new TreeMap<String, ClassReader>();
+        TreeMap<String, ClassReader> keep = new TreeMap<>();
+        TreeMap<String, ClassReader> new_keep = new TreeMap<>();
+        TreeMap<String, ClassReader> in_deps = new TreeMap<>();
+        TreeMap<String, ClassReader> out_deps = new TreeMap<>();
 
         ClassReader cr = mAa.findClass("mock_android.widget.LinearLayout", zipClasses, keep);
         DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 8a2235b..c4dd7ee 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -18,12 +18,6 @@
 package com.android.tools.layoutlib.create;
 
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -31,7 +25,6 @@
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 
 import java.io.ByteArrayOutputStream;
@@ -44,7 +37,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
@@ -52,11 +44,18 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 /**
  * Unit tests for some methods of {@link AsmGenerator}.
  */
 public class AsmGeneratorTest {
 
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
     private MockLog mLog;
     private ArrayList<String> mOsJarPath;
     private String mOsDestJar;
@@ -70,7 +69,8 @@
         mLog = new MockLog();
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
 
-        mOsJarPath = new ArrayList<String>();
+        mOsJarPath = new ArrayList<>();
+        //noinspection ConstantConditions
         mOsJarPath.add(url.getFile());
 
         mTempFile = File.createTempFile("mock", ".jar");
@@ -98,18 +98,18 @@
 
             @Override
             public String[] getDelegateMethods() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getDelegateClassNatives() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getOverriddenMethods() {
                 // methods to force override
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
@@ -123,7 +123,7 @@
 
             @Override
             public String[] getJavaPkgClasses() {
-              return new String[0];
+              return EMPTY_STRING_ARRAY;
             }
 
             @Override
@@ -134,17 +134,17 @@
             @Override
             public String[] getDeleteReturns() {
                  // methods deleted from their return type.
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getPromotedFields() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
-                return new HashMap<String, InjectMethodRunnable>(0);
+                return Collections.emptyMap();
             }
         };
 
@@ -155,7 +155,7 @@
                 new String[] {        // include classes
                     "**"
                 },
-                new HashSet<String>(0) /* excluded classes */,
+                Collections.<String>emptySet() /* excluded classes */,
                 new String[]{} /* include files */);
         aa.analyze();
         agen.generate();
@@ -178,24 +178,24 @@
 
             @Override
             public String[] getDelegateMethods() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getDelegateClassNatives() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getOverriddenMethods() {
                 // methods to force override
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getRenamedClasses() {
                 // classes to rename (so that we can replace them)
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
@@ -214,17 +214,17 @@
             @Override
             public String[] getDeleteReturns() {
                  // methods deleted from their return type.
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getPromotedFields() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
-                return new HashMap<String, InjectMethodRunnable>(0);
+                return Collections.emptyMap();
             }
         };
 
@@ -235,14 +235,14 @@
                 new String[] {        // include classes
                     "**"
                 },
-                new HashSet<String>(1),
+                Collections.<String>emptySet(),
                 new String[] {        /* include files */
                     "mock_android/data/data*"
                 });
         aa.analyze();
         agen.generate();
-        Map<String, ClassReader> output = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> output = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
         parseZip(mOsDestJar, output, filesFound);
         boolean injectedClassFound = false;
         for (ClassReader cr: output.values()) {
@@ -265,35 +265,35 @@
 
             @Override
             public String[] getDelegateMethods() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getDelegateClassNatives() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getOverriddenMethods() {
                 // methods to force override
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getRenamedClasses() {
                 // classes to rename (so that we can replace them)
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getJavaPkgClasses() {
                 // classes to refactor (so that we can replace them)
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public Set<String> getExcludedClasses() {
-                Set<String> set = new HashSet<String>(2);
+                Set<String> set = new HashSet<>(2);
                 set.add("mock_android.dummy.InnerTest");
                 set.add("java.lang.JavaClass");
                 return set;
@@ -302,17 +302,17 @@
             @Override
             public String[] getDeleteReturns() {
                 // methods deleted from their return type.
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getPromotedFields() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
-                return new HashMap<String, InjectMethodRunnable>(0);
+                return Collections.emptyMap();
             }
         };
 
@@ -329,8 +329,8 @@
                 });
         aa.analyze();
         agen.generate();
-        Map<String, ClassReader> output = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> output = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
         parseZip(mOsDestJar, output, filesFound);
         for (String s : output.keySet()) {
             assertFalse(excludedClasses.contains(s));
@@ -351,55 +351,52 @@
 
             @Override
             public String[] getDelegateMethods() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getDelegateClassNatives() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getOverriddenMethods() {
                 // methods to force override
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getRenamedClasses() {
                 // classes to rename (so that we can replace them)
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getJavaPkgClasses() {
                 // classes to refactor (so that we can replace them)
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public Set<String> getExcludedClasses() {
-                return new HashSet<String>(0);
+                return Collections.emptySet();
             }
 
             @Override
             public String[] getDeleteReturns() {
                 // methods deleted from their return type.
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public String[] getPromotedFields() {
-                return new String[0];
+                return EMPTY_STRING_ARRAY;
             }
 
             @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
-                HashMap<String, InjectMethodRunnable> map =
-                        new HashMap<String, InjectMethodRunnable>(1);
-                map.put("mock_android.util.EmptyArray",
+                return Collections.singletonMap("mock_android.util.EmptyArray",
                         InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER);
-                return map;
             }
         };
 
@@ -415,8 +412,8 @@
                 });
         aa.analyze();
         agen.generate();
-        Map<String, ClassReader> output = new TreeMap<String, ClassReader>();
-        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        Map<String, ClassReader> output = new TreeMap<>();
+        Map<String, InputStream> filesFound = new TreeMap<>();
         parseZip(mOsDestJar, output, filesFound);
         final String modifiedClass = "mock_android.util.EmptyArray";
         final String modifiedClassPath = modifiedClass.replace('.', '/').concat(".class");
@@ -424,11 +421,8 @@
         ZipEntry entry = zipFile.getEntry(modifiedClassPath);
         assertNotNull(entry);
         final byte[] bytes;
-        final InputStream inputStream = zipFile.getInputStream(entry);
-        try {
+        try (InputStream inputStream = zipFile.getInputStream(entry)) {
             bytes = getByteArray(inputStream);
-        } finally {
-            inputStream.close();
         }
         ClassLoader classLoader = new ClassLoader(getClass().getClassLoader()) {
             @Override
@@ -489,7 +483,7 @@
         boolean mInjectedClassFound = false;
 
         TestClassVisitor() {
-            super(Opcodes.ASM4);
+            super(Main.ASM_VERSION);
         }
 
         @Override
@@ -514,7 +508,7 @@
         public MethodVisitor visitMethod(int access, String name, String desc,
                 String signature, String[] exceptions) {
             MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
-            return new MethodVisitor(Opcodes.ASM4, mv) {
+            return new MethodVisitor(Main.ASM_VERSION, mv) {
 
                 @Override
                 public void visitFieldInsn(int opcode, String owner, String name,
@@ -540,10 +534,10 @@
 
                 @Override
                 public void visitMethodInsn(int opcode, String owner, String name,
-                        String desc) {
+                        String desc, boolean itf) {
                     assertTrue(!getBase(owner).equals(JAVA_CLASS_NAME));
                     assertTrue(testType(Type.getType(desc)));
-                    super.visitMethodInsn(opcode, owner, name, desc);
+                    super.visitMethodInsn(opcode, owner, name, desc, itf);
                 }
 
             };
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
index 0135c40..0cdcdc0 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
@@ -60,7 +60,7 @@
      * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found.
      */
     private static class MockClassHasNativeVisitor extends ClassHasNativeVisitor {
-        private ArrayList<String> mMethodsFound = new ArrayList<String>();
+        private ArrayList<String> mMethodsFound = new ArrayList<>();
 
         public String[] getMethodsFound() {
             return mMethodsFound.toArray(new String[mMethodsFound.size()]);
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index e37a09b..0912fb1 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -88,7 +88,7 @@
         // Now process it but tell the delegate to not modify any method
         ClassWriter cw = new ClassWriter(0 /*flags*/);
 
-        HashSet<String> delegateMethods = new HashSet<String>();
+        HashSet<String> delegateMethods = new HashSet<>();
         String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
         DelegateClassAdapter cv = new DelegateClassAdapter(
                 mLog, cw, internalClassName, delegateMethods);
@@ -152,7 +152,7 @@
 
         String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
 
-        HashSet<String> delegateMethods = new HashSet<String>();
+        HashSet<String> delegateMethods = new HashSet<>();
         delegateMethods.add("<init>");
         DelegateClassAdapter cv = new DelegateClassAdapter(
                 mLog, cw, internalClassName, delegateMethods);
@@ -166,7 +166,7 @@
         ClassWriter cw = new ClassWriter(0 /*flags*/);
         String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
 
-        HashSet<String> delegateMethods = new HashSet<String>();
+        HashSet<String> delegateMethods = new HashSet<>();
         delegateMethods.add(DelegateClassAdapter.ALL_NATIVES);
         DelegateClassAdapter cv = new DelegateClassAdapter(
                 mLog, cw, internalClassName, delegateMethods);
@@ -217,7 +217,7 @@
     @Test
     public void testDelegateInner() throws Throwable {
         // We'll delegate the "get" method of both the inner and outer class.
-        HashSet<String> delegateMethods = new HashSet<String>();
+        HashSet<String> delegateMethods = new HashSet<>();
         delegateMethods.add("get");
         delegateMethods.add("privateMethod");
 
@@ -300,7 +300,7 @@
     @Test
     public void testDelegateStaticInner() throws Throwable {
         // We'll delegate the "get" method of both the inner and outer class.
-        HashSet<String> delegateMethods = new HashSet<String>();
+        HashSet<String> delegateMethods = new HashSet<>();
         delegateMethods.add("get");
 
         // Generate the delegate for the outer class.
@@ -367,7 +367,7 @@
      */
     private abstract class ClassLoader2 extends ClassLoader {
 
-        private final Map<String, byte[]> mClassDefs = new HashMap<String, byte[]>();
+        private final Map<String, byte[]> mClassDefs = new HashMap<>();
 
         public ClassLoader2() {
             super(null);
diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk
new file mode 100644
index 0000000..35d28fb
--- /dev/null
+++ b/tools/preload2/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+# To connect to devices (and take hprof dumps).
+LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt
+
+# To process hprof dumps.
+LOCAL_STATIC_JAVA_LIBRARIES += perflib-prebuilt trove-prebuilt guavalib
+
+# For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
+# convenience (and to not depend on internal JDK APIs).
+LOCAL_STATIC_JAVA_LIBRARIES += apache-harmony-jdwp-tests-host junit
+
+LOCAL_MODULE:= preload2
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Copy the preload-tool shell script to the host's bin directory.
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := preload-tool
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/preload-tool $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
new file mode 100644
index 0000000..36dbc1c
--- /dev/null
+++ b/tools/preload2/preload-tool
@@ -0,0 +1,37 @@
+# Copyright (C) 2015 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 script is used on the host only. It uses a common subset
+# shell dialect that should work well. It is partially derived
+# from art/tools/art.
+
+function follow_links() {
+  if [ z"$BASH_SOURCE" != z ]; then
+    file="$BASH_SOURCE"
+  else
+    file="$0"
+  fi
+  while [ -h "$file" ]; do
+    # On Mac OS, readlink -f doesn't work.
+    file="$(readlink "$file")"
+  done
+  echo "$file"
+}
+
+
+PROG_NAME="$(follow_links)"
+PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
+ANDROID_ROOT=$PROG_DIR/..
+
+java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main
diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java
new file mode 100644
index 0000000..71ef025
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ClientUtils.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 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.preload;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+
+/**
+ * Helper class for common communication with a Client (the ddms name for a running application).
+ *
+ * Instances take a default timeout parameter that's applied to all functions without explicit
+ * timeout. Timeouts are in milliseconds.
+ */
+public class ClientUtils {
+
+    private int defaultTimeout;
+
+    public ClientUtils() {
+        this(10000);
+    }
+
+    public ClientUtils(int defaultTimeout) {
+        this.defaultTimeout = defaultTimeout;
+    }
+
+    /**
+     * Shortcut for findClient with default timeout.
+     */
+    public Client findClient(IDevice device, String processName, int processPid) {
+        return findClient(device, processName, processPid, defaultTimeout);
+    }
+
+    /**
+     * Find the client with the given process name or process id. The name takes precedence over
+     * the process id (if valid). Stop looking after the given timeout.
+     *
+     * @param device The device to communicate with.
+     * @param processName The name of the process. May be null.
+     * @param processPid The pid of the process. Values less than or equal to zero are ignored.
+     * @param timeout The amount of milliseconds to wait, at most.
+     * @return The client, if found. Otherwise null.
+     */
+    public Client findClient(IDevice device, String processName, int processPid, int timeout) {
+        WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
+        return wfc.get();
+    }
+
+    /**
+     * Shortcut for findAllClients with default timeout.
+     */
+    public Client[] findAllClients(IDevice device) {
+        return findAllClients(device, defaultTimeout);
+    }
+
+    /**
+     * Retrieve all clients known to the given device. Wait at most the given timeout.
+     *
+     * @param device The device to investigate.
+     * @param timeout The amount of milliseconds to wait, at most.
+     * @return An array of clients running on the given device. May be null depending on the
+     *         device implementation.
+     */
+    public Client[] findAllClients(IDevice device, int timeout) {
+        if (device.hasClients()) {
+            return device.getClients();
+        }
+        WaitForClients wfc = new WaitForClients(device, timeout);
+        return wfc.get();
+    }
+
+    private static class WaitForClient implements IClientChangeListener {
+
+        private IDevice device;
+        private String processName;
+        private int processPid;
+        private long timeout;
+        private Client result;
+
+        public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
+            this.device = device;
+            this.processName = processName;
+            this.processPid = processPid;
+            this.timeout = timeout;
+            this.result = null;
+        }
+
+        public Client get() {
+            synchronized (this) {
+                AndroidDebugBridge.addClientChangeListener(this);
+
+                // Maybe it's already there.
+                if (result == null) {
+                    result = searchForClient(device);
+                }
+
+                if (result == null) {
+                    try {
+                        wait(timeout);
+                    } catch (InterruptedException e) {
+                        // Note: doesn't guard for spurious wakeup.
+                    }
+                }
+            }
+
+            AndroidDebugBridge.removeClientChangeListener(this);
+            return result;
+        }
+
+        private Client searchForClient(IDevice device) {
+            if (processName != null) {
+                Client tmp = device.getClient(processName);
+                if (tmp != null) {
+                    return tmp;
+                }
+            }
+            if (processPid > 0) {
+                String name = device.getClientName(processPid);
+                if (name != null && !name.isEmpty()) {
+                    Client tmp = device.getClient(name);
+                    if (tmp != null) {
+                        return tmp;
+                    }
+                }
+            }
+            if (processPid > 0) {
+                // Try manual search.
+                for (Client cl : device.getClients()) {
+                    if (cl.getClientData().getPid() == processPid
+                            && cl.getClientData().getClientDescription() != null) {
+                        return cl;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private boolean isTargetClient(Client c) {
+            if (processPid > 0 && c.getClientData().getPid() == processPid) {
+                return true;
+            }
+            if (processName != null
+                    && processName.equals(c.getClientData().getClientDescription())) {
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void clientChanged(Client arg0, int arg1) {
+            synchronized (this) {
+                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
+                    if (isTargetClient(arg0)) {
+                        result = arg0;
+                        notifyAll();
+                    }
+                }
+            }
+        }
+    }
+
+    private static class WaitForClients implements IClientChangeListener {
+
+        private IDevice device;
+        private long timeout;
+
+        public WaitForClients(IDevice device, long timeout) {
+            this.device = device;
+            this.timeout = timeout;
+        }
+
+        public Client[] get() {
+            synchronized (this) {
+                AndroidDebugBridge.addClientChangeListener(this);
+
+                if (device.hasClients()) {
+                    return device.getClients();
+                }
+
+                try {
+                    wait(timeout); // Note: doesn't guard for spurious wakeup.
+                } catch (InterruptedException exc) {
+                }
+
+                // We will be woken up when the first client data arrives. Sleep a little longer
+                // to give (hopefully all of) the rest of the clients a chance to become available.
+                // Note: a loop with timeout is brittle as well and complicated, just accept this
+                //       for now.
+                try {
+                    Thread.sleep(500);
+                } catch (InterruptedException exc) {
+                }
+            }
+
+            AndroidDebugBridge.removeClientChangeListener(this);
+
+            return device.getClients();
+        }
+
+        @Override
+        public void clientChanged(Client arg0, int arg1) {
+            synchronized (this) {
+                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
+                    notifyAll();
+                }
+            }
+        }
+    }
+}
diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java
new file mode 100644
index 0000000..72de7b5
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/DeviceUtils.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2015 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.preload;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.preload.classdataretrieval.hprof.Hprof;
+import com.android.ddmlib.DdmPreferences;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.IShellOutputReceiver;
+
+import java.util.Date;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper class for some device routines.
+ */
+public class DeviceUtils {
+
+  public static void init(int debugPort) {
+    DdmPreferences.setSelectedDebugPort(debugPort);
+
+    Hprof.init();
+
+    AndroidDebugBridge.init(true);
+
+    AndroidDebugBridge.createBridge();
+  }
+
+  /**
+   * Run a command in the shell on the device.
+   */
+  public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
+    doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
+  }
+
+  /**
+   * Run a command in the shell on the device. Collects and returns the console output.
+   */
+  public static String doShellReturnString(IDevice device, String cmdline, long timeout,
+      TimeUnit unit) {
+    CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
+    doShell(device, cmdline, rec, timeout, unit);
+    return rec.toString();
+  }
+
+  /**
+   * Run a command in the shell on the device, directing all output to the given receiver.
+   */
+  public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
+      long timeout, TimeUnit unit) {
+    try {
+      device.executeShellCommand(cmdline, receiver, timeout, unit);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Run am start on the device.
+   */
+  public static void doAMStart(IDevice device, String name, String activity) {
+    doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
+  }
+
+  /**
+   * Find the device with the given serial. Give up after the given timeout (in milliseconds).
+   */
+  public static IDevice findDevice(String serial, int timeout) {
+    WaitForDevice wfd = new WaitForDevice(serial, timeout);
+    return wfd.get();
+  }
+
+  /**
+   * Get all devices ddms knows about. Wait at most for the given timeout.
+   */
+  public static IDevice[] findDevices(int timeout) {
+    WaitForDevice wfd = new WaitForDevice(null, timeout);
+    wfd.get();
+    return AndroidDebugBridge.getBridge().getDevices();
+  }
+
+  /**
+   * Return the build type of the given device. This is the value of the "ro.build.type"
+   * system property.
+   */
+  public static String getBuildType(IDevice device) {
+    try {
+      Future<String> buildType = device.getSystemProperty("ro.build.type");
+      return buildType.get(500, TimeUnit.MILLISECONDS);
+    } catch (Exception e) {
+    }
+    return null;
+  }
+
+  /**
+   * Check whether the given device has a pre-optimized boot image. More precisely, checks
+   * whether /system/framework/ * /boot.art exists.
+   */
+  public static boolean hasPrebuiltBootImage(IDevice device) {
+    String ret =
+        doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
+
+    return !ret.contains("No such file or directory");
+  }
+
+  /**
+   * Remove files involved in a standard build that interfere with collecting data. This will
+   * remove /etc/preloaded-classes, which determines which classes are allocated already in the
+   * boot image. It also deletes any compiled boot image on the device. Then it restarts the
+   * device.
+   *
+   * This is a potentially long-running operation, as the boot after the deletion may take a while.
+   * The method will abort after the given timeout.
+   */
+  public static boolean removePreloaded(IDevice device, long preloadedWaitTimeInSeconds) {
+    String oldContent =
+        DeviceUtils.doShellReturnString(device, "cat /etc/preloaded-classes", 1, TimeUnit.SECONDS);
+    if (oldContent.trim().equals("")) {
+      System.out.println("Preloaded-classes already empty.");
+      return true;
+    }
+
+    // Stop the system server etc.
+    doShell(device, "stop", 100, TimeUnit.MILLISECONDS);
+
+    // Remount /system, delete /etc/preloaded-classes. It would be nice to use "adb remount,"
+    // but AndroidDebugBridge doesn't expose it.
+    doShell(device, "mount -o remount,rw /system", 500, TimeUnit.MILLISECONDS);
+    doShell(device, "rm /etc/preloaded-classes", 100, TimeUnit.MILLISECONDS);
+    // We do need an empty file.
+    doShell(device, "touch /etc/preloaded-classes", 100, TimeUnit.MILLISECONDS);
+
+    // Delete the files in the dalvik cache.
+    doShell(device, "rm /data/dalvik-cache/*/*boot.art", 500, TimeUnit.MILLISECONDS);
+
+    // We'll try to use dev.bootcomplete to know when the system server is back up. But stop
+    // doesn't reset it, so do it manually.
+    doShell(device, "setprop dev.bootcomplete \"0\"", 500, TimeUnit.MILLISECONDS);
+
+    // Start the system server.
+    doShell(device, "start", 100, TimeUnit.MILLISECONDS);
+
+    // Do a loop checking each second whether bootcomplete. Wait for at most the given
+    // threshold.
+    Date startDate = new Date();
+    for (;;) {
+      try {
+        Thread.sleep(1000);
+      } catch (InterruptedException e) {
+        // Ignore spurious wakeup.
+      }
+      // Check whether bootcomplete.
+      String ret =
+          doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
+      if (ret.trim().equals("1")) {
+        break;
+      }
+      System.out.println("Still not booted: " + ret);
+
+      // Check whether we timed out. This is a simplistic check that doesn't take into account
+      // things like switches in time.
+      Date endDate = new Date();
+      long seconds =
+          TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
+      if (seconds > preloadedWaitTimeInSeconds) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Enable method-tracing on device. The system should be restarted after this.
+   */
+  public static void enableTracing(IDevice device) {
+    // Disable selinux.
+    doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
+
+    // Make the profile directory world-writable.
+    doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
+
+    // Enable streaming method tracing with a small 1K buffer.
+    doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
+    doShell(device, "setprop dalvik.vm.method-trace-file "
+                    + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
+    doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
+    doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
+  }
+
+  private static class NullShellOutputReceiver implements IShellOutputReceiver {
+    @Override
+    public boolean isCancelled() {
+      return false;
+    }
+
+    @Override
+    public void flush() {}
+
+    @Override
+    public void addOutput(byte[] arg0, int arg1, int arg2) {}
+  }
+
+  private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
+
+    private StringBuilder builder = new StringBuilder();
+
+    @Override
+    public String toString() {
+      String ret = builder.toString();
+      // Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
+      while (ret.endsWith("\r") || ret.endsWith("\n")) {
+        ret = ret.substring(0, ret.length() - 1);
+      }
+      return ret;
+    }
+
+    @Override
+    public void addOutput(byte[] arg0, int arg1, int arg2) {
+      builder.append(new String(arg0, arg1, arg2));
+    }
+
+    @Override
+    public void flush() {}
+
+    @Override
+    public boolean isCancelled() {
+      return false;
+    }
+  }
+
+  private static class WaitForDevice {
+
+    private String serial;
+    private long timeout;
+    private IDevice device;
+
+    public WaitForDevice(String serial, long timeout) {
+      this.serial = serial;
+      this.timeout = timeout;
+      device = null;
+    }
+
+    public IDevice get() {
+      if (device == null) {
+          WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
+          synchronized (wfdl) {
+              AndroidDebugBridge.addDeviceChangeListener(wfdl);
+
+              // Check whether we already know about this device.
+              IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
+              if (serial != null) {
+                  for (IDevice d : devices) {
+                      if (serial.equals(d.getSerialNumber())) {
+                          // Only accept if there are clients already. Else wait for the callback informing
+                          // us that we now have clients.
+                          if (d.hasClients()) {
+                              device = d;
+                          }
+
+                          break;
+                      }
+                  }
+              } else {
+                  if (devices.length > 0) {
+                      device = devices[0];
+                  }
+              }
+
+              if (device == null) {
+                  try {
+                      wait(timeout);
+                  } catch (InterruptedException e) {
+                      // Ignore spurious wakeups.
+                  }
+                  device = wfdl.getDevice();
+              }
+
+              AndroidDebugBridge.removeDeviceChangeListener(wfdl);
+          }
+      }
+
+      if (device != null) {
+          // Wait for clients.
+          WaitForClientsListener wfcl = new WaitForClientsListener(device);
+          synchronized (wfcl) {
+              AndroidDebugBridge.addDeviceChangeListener(wfcl);
+
+              if (!device.hasClients()) {
+                  try {
+                      wait(timeout);
+                  } catch (InterruptedException e) {
+                      // Ignore spurious wakeups.
+                  }
+              }
+
+              AndroidDebugBridge.removeDeviceChangeListener(wfcl);
+          }
+      }
+
+      return device;
+    }
+
+    private static class WaitForDeviceListener implements IDeviceChangeListener {
+
+        private String serial;
+        private IDevice device;
+
+        public WaitForDeviceListener(String serial) {
+            this.serial = serial;
+        }
+
+        public IDevice getDevice() {
+            return device;
+        }
+
+        @Override
+        public void deviceChanged(IDevice arg0, int arg1) {
+            // We may get a device changed instead of connected. Handle like a connection.
+            deviceConnected(arg0);
+        }
+
+        @Override
+        public void deviceConnected(IDevice arg0) {
+            if (device != null) {
+                // Ignore updates.
+                return;
+            }
+
+            if (serial == null || serial.equals(arg0.getSerialNumber())) {
+                device = arg0;
+                synchronized (this) {
+                    notifyAll();
+                }
+            }
+        }
+
+        @Override
+        public void deviceDisconnected(IDevice arg0) {
+            // Ignore disconnects.
+        }
+
+    }
+
+    private static class WaitForClientsListener implements IDeviceChangeListener {
+
+        private IDevice myDevice;
+
+        public WaitForClientsListener(IDevice myDevice) {
+            this.myDevice = myDevice;
+        }
+
+        @Override
+        public void deviceChanged(IDevice arg0, int arg1) {
+            if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
+                // Got a client list, done here.
+                synchronized (this) {
+                    notifyAll();
+                }
+            }
+        }
+
+        @Override
+        public void deviceConnected(IDevice arg0) {
+        }
+
+        @Override
+        public void deviceDisconnected(IDevice arg0) {
+        }
+
+    }
+  }
+
+}
diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java
new file mode 100644
index 0000000..d997224
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/DumpData.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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.preload;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Holds the collected data for a process.
+ */
+public class DumpData {
+    /**
+     * Name of the package (=application).
+     */
+    String packageName;
+
+    /**
+     * A map of class name to a string for the classloader. This may be a toString equivalent,
+     * or just a unique ID.
+     */
+    Map<String, String> dumpData;
+
+    /**
+     * The Date when this data was captured. Mostly for display purposes.
+     */
+    Date date;
+
+    /**
+     * A cached value for the number of boot classpath classes (classloader value in dumpData is
+     * null).
+     */
+    int bcpClasses;
+
+    public DumpData(String packageName, Map<String, String> dumpData, Date date) {
+        this.packageName = packageName;
+        this.dumpData = dumpData;
+        this.date = date;
+
+        countBootClassPath();
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public Map<String, String> getDumpData() {
+        return dumpData;
+    }
+
+    public void countBootClassPath() {
+        bcpClasses = 0;
+        for (Map.Entry<String, String> e : dumpData.entrySet()) {
+            if (e.getValue() == null) {
+                bcpClasses++;
+            }
+        }
+    }
+
+    // Return an inverted mapping.
+    public Map<String, Set<String>> invertData() {
+        Map<String, Set<String>> ret = new HashMap<>();
+        for (Map.Entry<String, String> e : dumpData.entrySet()) {
+            if (!ret.containsKey(e.getValue())) {
+                ret.put(e.getValue(), new HashSet<String>());
+            }
+            ret.get(e.getValue()).add(e.getKey());
+        }
+        return ret;
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java
new file mode 100644
index 0000000..28625c5
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/DumpDataIO.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.preload;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.File;
+import java.io.FileReader;
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Helper class for serialization and deserialization of a collection of DumpData objects to XML.
+ */
+public class DumpDataIO {
+
+  /**
+   * Serialize the given collection to an XML document. Returns the produced string.
+   */
+  public static String serialize(Collection<DumpData> data) {
+      // We'll do this by hand, constructing a DOM or similar is too complicated for our simple
+      // use case.
+
+      StringBuilder sb = new StringBuilder();
+      sb.append("<preloaded-classes-data>\n");
+
+      for (DumpData d : data) {
+          serialize(d, sb);
+      }
+
+      sb.append("</preloaded-classes-data>\n");
+      return sb.toString();
+  }
+
+  private static void serialize(DumpData d, StringBuilder sb) {
+      sb.append("<data package=\"" + d.packageName + "\" date=\"" +
+              DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
+
+      for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
+          sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
+      }
+
+      sb.append("</data>\n");
+  }
+
+  /**
+   * Load a collection of DumpData objects from the given file.
+   */
+  public static Collection<DumpData> deserialize(File f) throws Exception {
+      // Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
+
+      SAXParserFactory spf = SAXParserFactory.newInstance();
+      spf.setNamespaceAware(false);
+      SAXParser saxParser = spf.newSAXParser();
+
+      XMLReader xmlReader = saxParser.getXMLReader();
+      DumpDataContentHandler ddch = new DumpDataContentHandler();
+      xmlReader.setContentHandler(ddch);
+      xmlReader.parse(new InputSource(new FileReader(f)));
+
+      return ddch.data;
+  }
+
+  private static class DumpDataContentHandler extends DefaultHandler {
+      Collection<DumpData> data = new LinkedList<DumpData>();
+      DumpData openData = null;
+
+      @Override
+      public void startElement(String uri, String localName, String qName, Attributes attributes)
+              throws SAXException {
+          if (qName.equals("data")) {
+              if (openData != null) {
+                  throw new IllegalStateException();
+              }
+              String pkg = attributes.getValue("package");
+              String dateString = attributes.getValue("date");
+
+              if (pkg == null || dateString == null) {
+                  throw new IllegalArgumentException();
+              }
+
+              try {
+                  Date date = DateFormat.getDateTimeInstance().parse(dateString);
+                  openData = new DumpData(pkg, new HashMap<String, String>(), date);
+              } catch (Exception e) {
+                  throw new RuntimeException(e);
+              }
+          } else if (qName.equals("class")) {
+              if (openData == null) {
+                  throw new IllegalStateException();
+              }
+              String className = attributes.getValue("name");
+              String classLoader = attributes.getValue("classloader");
+
+              if (className == null || classLoader == null) {
+                  throw new IllegalArgumentException();
+              }
+
+              openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
+          }
+      }
+
+      @Override
+      public void endElement(String uri, String localName, String qName) throws SAXException {
+          if (qName.equals("data")) {
+              if (openData == null) {
+                  throw new IllegalStateException();
+              }
+              openData.countBootClassPath();
+
+              data.add(openData);
+              openData = null;
+          }
+      }
+  }
+}
diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java
new file mode 100644
index 0000000..d97cbf0
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/DumpTableModel.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 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.preload;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+/**
+ * A table model for collected DumpData. This is both the internal storage as well as the model
+ * for display.
+ */
+public class DumpTableModel extends AbstractTableModel {
+
+    private List<DumpData> data = new ArrayList<DumpData>();
+
+    public void addData(DumpData d) {
+        data.add(d);
+        fireTableRowsInserted(data.size() - 1, data.size() - 1);
+    }
+
+    public void clear() {
+        int size = data.size();
+        if (size > 0) {
+            data.clear();
+            fireTableRowsDeleted(0, size - 1);
+        }
+    }
+
+    public List<DumpData> getData() {
+        return data;
+    }
+
+    @Override
+    public int getRowCount() {
+        return data.size();
+    }
+
+    @Override
+    public int getColumnCount() {
+        return 4;
+    }
+
+    @Override
+    public String getColumnName(int column) {
+        switch (column) {
+            case 0:
+                return "Package";
+            case 1:
+                return "Date";
+            case 2:
+                return "# All Classes";
+            case 3:
+                return "# Boot Classpath Classes";
+
+            default:
+                throw new IndexOutOfBoundsException(String.valueOf(column));
+        }
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        DumpData d = data.get(rowIndex);
+        switch (columnIndex) {
+            case 0:
+                return d.packageName;
+            case 1:
+                return d.date;
+            case 2:
+                return d.dumpData.size();
+            case 3:
+                return d.bcpClasses;
+
+            default:
+                throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
new file mode 100644
index 0000000..ca5b0e0
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/Main.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 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.preload;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.preload.actions.ClearTableAction;
+import com.android.preload.actions.ComputeThresholdAction;
+import com.android.preload.actions.ComputeThresholdXAction;
+import com.android.preload.actions.DeviceSpecific;
+import com.android.preload.actions.ExportAction;
+import com.android.preload.actions.ImportAction;
+import com.android.preload.actions.ReloadListAction;
+import com.android.preload.actions.RunMonkeyAction;
+import com.android.preload.actions.ScanAllPackagesAction;
+import com.android.preload.actions.ScanPackageAction;
+import com.android.preload.actions.ShowDataAction;
+import com.android.preload.classdataretrieval.ClassDataRetriever;
+import com.android.preload.classdataretrieval.hprof.Hprof;
+import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
+import com.android.preload.ui.UI;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.Action;
+import javax.swing.DefaultListModel;
+
+public class Main {
+
+    /**
+     * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
+     * off for now.
+     */
+    public final static boolean ENABLE_TRACING = false;
+
+    /**
+     * Ten-second timeout.
+     */
+    public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
+
+    /**
+     * Hprof timeout. Two minutes.
+     */
+    public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
+
+    private IDevice device;
+    private static ClientUtils clientUtils;
+
+    private DumpTableModel dataTableModel;
+    private DefaultListModel<Client> clientListModel;
+
+    private UI ui;
+
+    // Actions that need to be updated once a device is selected.
+    private Collection<DeviceSpecific> deviceSpecificActions;
+
+    // Current main instance.
+    private static Main top;
+    private static boolean useJdwpClassDataRetriever = false;
+
+    public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
+            + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
+            + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
+
+
+            // Threads
+            "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
+            + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
+            + "(.*\\$NoPreloadHolder$)";
+
+    /**
+     * @param args
+     */
+    public static void main(String[] args) {
+        Main m = new Main();
+        top = m;
+
+        m.startUp();
+    }
+
+    public Main() {
+        clientListModel = new DefaultListModel<Client>();
+        dataTableModel = new DumpTableModel();
+
+        clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS);  // Client utils with 10s timeout.
+
+        List<Action> actions = new ArrayList<Action>();
+        actions.add(new ReloadListAction(clientUtils, null, clientListModel));
+        actions.add(new ClearTableAction(dataTableModel));
+        actions.add(new RunMonkeyAction(null, dataTableModel));
+        actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
+        actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
+        actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
+                CLASS_PRELOAD_BLACKLIST));
+        actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
+                null));
+        actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
+                CLASS_PRELOAD_BLACKLIST));
+        actions.add(new ShowDataAction(dataTableModel));
+        actions.add(new ImportAction(dataTableModel));
+        actions.add(new ExportAction(dataTableModel));
+
+        deviceSpecificActions = new ArrayList<DeviceSpecific>();
+        for (Action a : actions) {
+            if (a instanceof DeviceSpecific) {
+                deviceSpecificActions.add((DeviceSpecific)a);
+            }
+        }
+
+        ui = new UI(clientListModel, dataTableModel, actions);
+        ui.setVisible(true);
+    }
+
+    public static UI getUI() {
+        return top.ui;
+    }
+
+    public static ClassDataRetriever getClassDataRetriever() {
+        if (useJdwpClassDataRetriever) {
+            return new JDWPClassDataRetriever();
+        } else {
+            return new Hprof(HPROF_TIMEOUT_MILLIS);
+        }
+    }
+
+    public IDevice getDevice() {
+        return device;
+    }
+
+    public void setDevice(IDevice device) {
+        this.device = device;
+        for (DeviceSpecific ds : deviceSpecificActions) {
+            ds.setDevice(device);
+        }
+    }
+
+    public DefaultListModel<Client> getClientListModel() {
+        return clientListModel;
+    }
+
+    static class DeviceWrapper {
+        IDevice device;
+
+        public DeviceWrapper(IDevice d) {
+            device = d;
+        }
+
+        @Override
+        public String toString() {
+            return device.getName() + " (#" + device.getSerialNumber() + ")";
+        }
+    }
+
+    private void startUp() {
+        getUI().showWaitDialog();
+        initDevice();
+
+        // Load clients.
+        new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
+
+        getUI().hideWaitDialog();
+    }
+
+    private void initDevice() {
+        DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
+
+        IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
+        if (devices == null || devices.length == 0) {
+            throw new RuntimeException("Could not find any devices...");
+        }
+
+        getUI().hideWaitDialog();
+
+        DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
+        for (int i = 0; i < devices.length; i++) {
+            deviceWrappers[i] = new DeviceWrapper(devices[i]);
+        }
+
+        DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
+                deviceWrappers);
+        if (ret != null) {
+            setDevice(ret.device);
+        } else {
+            System.exit(0);
+        }
+
+        boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
+                "Do you want to prepare the device? This is highly recommended.");
+        if (prepare) {
+            String buildType = DeviceUtils.getBuildType(device);
+            if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
+                Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
+                        + ")");
+                return;
+            }
+            if (DeviceUtils.hasPrebuiltBootImage(device)) {
+                Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
+                        + "image!");
+                return;
+            }
+
+            if (ENABLE_TRACING) {
+                DeviceUtils.enableTracing(device);
+            }
+
+            Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
+                    + "long time. Please be patient.");
+            if (!DeviceUtils.removePreloaded(device, 15 * 60) /* 15m timeout */) {
+                Main.getUI().showMessageDialog("Removing preloaded-classes failed unexpectedly!");
+            }
+        }
+    }
+
+    public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
+            throws Exception {
+        Client client = clientUtils.findClient(device, packageName, -1);
+        if (client == null) {
+            throw new RuntimeException("Could not find client...");
+        }
+        System.out.println("Found client: " + client);
+
+        return getClassDataRetriever().getClassData(client);
+    }
+
+}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
new file mode 100644
index 0000000..fbf83d2
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+public abstract class AbstractThreadedAction extends AbstractAction implements Runnable {
+
+    protected AbstractThreadedAction(String title) {
+        super(title);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        new Thread(this).start();
+    }
+
+}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
new file mode 100644
index 0000000..7906417
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.ddmlib.IDevice;
+
+import java.awt.event.ActionEvent;
+
+public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction
+        implements DeviceSpecific {
+
+    protected IDevice device;
+
+    protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) {
+        super(title);
+        this.device = device;
+    }
+
+    @Override
+    public void setDevice(IDevice device) {
+        this.device = device;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        if (device == null) {
+            return;
+        }
+        super.actionPerformed(e);
+    }
+}
diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
new file mode 100644
index 0000000..c0e4795
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.preload.DumpTableModel;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+public class ClearTableAction extends AbstractAction {
+    private final DumpTableModel dataTableModel;
+
+    public ClearTableAction(DumpTableModel dataTableModel) {
+        super("Clear");
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        dataTableModel.clear();
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
new file mode 100644
index 0000000..b524716
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.preload.DumpData;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+
+/**
+ * Compute an intersection of classes from the given data. A class is in the intersection if it
+ * appears in at least the number of threshold given packages. An optional blacklist can be
+ * used to filter classes from the intersection.
+ */
+public class ComputeThresholdAction extends AbstractAction implements Runnable {
+    protected int threshold;
+    private Pattern blacklist;
+    private DumpTableModel dataTableModel;
+
+    /**
+     * Create an action with the given parameters. The blacklist is a regular expression
+     * that filters classes.
+     */
+    public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold,
+            String blacklist) {
+        super(name);
+        this.dataTableModel = dataTableModel;
+        this.threshold = threshold;
+        if (blacklist != null) {
+            this.blacklist = Pattern.compile(blacklist);
+        }
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        List<DumpData> data = dataTableModel.getData();
+        if (data.size() == 0) {
+            Main.getUI().showMessageDialog("No data available, please scan packages or run "
+                    + "monkeys.");
+            return;
+        }
+        if (data.size() == 1) {
+            Main.getUI().showMessageDialog("Cannot compute list from only one data set, please "
+                    + "scan packages or run monkeys.");
+            return;
+        }
+
+        new Thread(this).start();
+    }
+
+    @Override
+    public void run() {
+        Main.getUI().showWaitDialog();
+
+        Map<String, Set<String>> uses = new HashMap<String, Set<String>>();
+        for (DumpData d : dataTableModel.getData()) {
+            Main.getUI().updateWaitDialog("Merging " + d.getPackageName());
+            updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData()));
+        }
+
+        Main.getUI().updateWaitDialog("Computing thresholded set");
+        Set<String> result = fromThreshold(uses, blacklist, threshold);
+        Main.getUI().hideWaitDialog();
+
+        boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
+                + " classes, would you like to save to disk?", "Save?");
+        if (ret) {
+            JFileChooser jfc = new JFileChooser();
+            int ret2 = jfc.showSaveDialog(Main.getUI());
+            if (ret2 == JFileChooser.APPROVE_OPTION) {
+                File f = jfc.getSelectedFile();
+                saveSet(result, f);
+            }
+        }
+    }
+
+    private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist,
+            int threshold) {
+        TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name.
+
+        for (Map.Entry<String, Set<String>> e : classUses.entrySet()) {
+            if (e.getValue().size() >= threshold) {
+                if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) {
+                    ret.add(e.getKey());
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    private static void updateClassUse(String pkg, Map<String, Set<String>> classUses,
+            Set<String> classes) {
+        for (String className : classes) {
+            Set<String> old = classUses.get(className);
+            if (old == null) {
+                classUses.put(className, new HashSet<String>());
+            }
+            classUses.get(className).add(pkg);
+        }
+    }
+
+    private static Set<String> getBootClassPathClasses(Map<String, String> source) {
+        Set<String> ret = new HashSet<>();
+        for (Map.Entry<String, String> e : source.entrySet()) {
+            if (e.getValue() == null) {
+                ret.add(e.getKey());
+            }
+        }
+        return ret;
+    }
+
+    private static void saveSet(Set<String> result, File f) {
+        try {
+            PrintWriter out = new PrintWriter(f);
+            for (String s : result) {
+                out.println(s);
+            }
+            out.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
new file mode 100644
index 0000000..3ec0a4c
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+public class ComputeThresholdXAction extends ComputeThresholdAction {
+
+    public ComputeThresholdXAction(String name, DumpTableModel dataTableModel,
+            String blacklist) {
+        super(name, dataTableModel, 1, blacklist);
+    }
+
+    @Override
+    public void run() {
+        String value = Main.getUI().showInputDialog("Threshold?");
+
+        if (value != null) {
+            try {
+                threshold = Integer.parseInt(value);
+                super.run();
+            } catch (Exception exc) {
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
new file mode 100644
index 0000000..35a8f26
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.ddmlib.IDevice;
+
+/**
+ * Marks an action as being device-specific. The user must set the device through the specified
+ * method if the device selection changes.
+ *
+ * Implementors must tolerate a null device (for example, with a no-op). This includes calling
+ * any methods before setDevice has been called.
+ */
+public interface DeviceSpecific {
+
+    /**
+     * Set the device that should be used. Note that there is no restriction on calling other
+     * methods of the implementor before a setDevice call. Neither is device guaranteed to be
+     * non-null.
+     *
+     * @param device The device to use going forward.
+     */
+    public void setDevice(IDevice device);
+}
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
new file mode 100644
index 0000000..cb8b3df
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ExportAction.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.preload.DumpDataIO;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.PrintWriter;
+
+import javax.swing.AbstractAction;
+
+public class ExportAction extends AbstractAction implements Runnable {
+    private File lastSaveFile;
+    private DumpTableModel dataTableModel;
+
+    public ExportAction(DumpTableModel dataTableModel) {
+        super("Export data");
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        lastSaveFile = Main.getUI().showSaveDialog();
+        if (lastSaveFile != null) {
+            new Thread(this).start();
+        }
+    }
+
+    @Override
+    public void run() {
+        Main.getUI().showWaitDialog();
+
+        String serialized = DumpDataIO.serialize(dataTableModel.getData());
+
+        if (serialized != null) {
+            try {
+                PrintWriter out = new PrintWriter(lastSaveFile);
+                out.println(serialized);
+                out.close();
+
+                Main.getUI().hideWaitDialog();
+            } catch (Exception e) {
+                Main.getUI().hideWaitDialog();
+                Main.getUI().showMessageDialog("Failed writing: " + e.getMessage());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
new file mode 100644
index 0000000..5c19765
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ImportAction.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.preload.DumpData;
+import com.android.preload.DumpDataIO;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.util.Collection;
+
+import javax.swing.AbstractAction;
+
+public class ImportAction extends AbstractAction implements Runnable {
+    private File[] lastOpenFiles;
+    private DumpTableModel dataTableModel;
+
+    public ImportAction(DumpTableModel dataTableModel) {
+        super("Import data");
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        lastOpenFiles = Main.getUI().showOpenDialog(true);
+        if (lastOpenFiles != null) {
+            new Thread(this).start();
+        }
+    }
+
+    @Override
+    public void run() {
+        Main.getUI().showWaitDialog();
+
+        try {
+            for (File f : lastOpenFiles) {
+                try {
+                    Collection<DumpData> data = DumpDataIO.deserialize(f);
+
+                    for (DumpData d : data) {
+                        dataTableModel.addData(d);
+                    }
+                } catch (Exception e) {
+                    Main.getUI().showMessageDialog("Failed reading: " + e.getMessage());
+                }
+            }
+        } finally {
+            Main.getUI().hideWaitDialog();
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
new file mode 100644
index 0000000..29f0557
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.preload.ClientUtils;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import javax.swing.DefaultListModel;
+
+public class ReloadListAction extends AbstractThreadedDeviceSpecificAction {
+
+    private ClientUtils clientUtils;
+    private final DefaultListModel<Client> clientListModel;
+
+    public ReloadListAction(ClientUtils utils, IDevice device,
+            DefaultListModel<Client> clientListModel) {
+        super("Reload", device);
+        this.clientUtils = utils;
+        this.clientListModel = clientListModel;
+    }
+
+    @Override
+    public void run() {
+        Client[] clients = clientUtils.findAllClients(device);
+        if (clients != null) {
+            Arrays.sort(clients, new ClientComparator());
+        }
+        clientListModel.removeAllElements();
+        for (Client c : clients) {
+            clientListModel.addElement(c);
+        }
+    }
+
+    private static class ClientComparator implements Comparator<Client> {
+
+        @Override
+        public int compare(Client o1, Client o2) {
+            String s1 = o1.getClientData().getClientDescription();
+            String s2 = o2.getClientData().getClientDescription();
+
+            if (s1 == null || s2 == null) {
+                // Not good, didn't get all data?
+                return (s1 == null) ? -1 : 1;
+            }
+
+            return s1.compareTo(s2);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
new file mode 100644
index 0000000..385e857
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.ddmlib.IDevice;
+import com.android.preload.DeviceUtils;
+import com.android.preload.DumpData;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.awt.event.ActionEvent;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.AbstractAction;
+
+public class RunMonkeyAction extends AbstractAction implements DeviceSpecific {
+
+    private final static String DEFAULT_MONKEY_PACKAGES =
+            "com.android.calendar,com.android.gallery3d";
+
+    private IDevice device;
+    private DumpTableModel dataTableModel;
+
+    public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) {
+        super("Run monkey");
+        this.device = device;
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void setDevice(IDevice device) {
+        this.device = device;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        String packages = Main.getUI().showInputDialog("Please enter packages name to run with"
+                + " the monkey, or leave empty for default.");
+        if (packages == null) {
+            return;
+        }
+        if (packages.isEmpty()) {
+            packages = DEFAULT_MONKEY_PACKAGES;
+        }
+        new Thread(new RunMonkeyRunnable(packages)).start();
+    }
+
+    private class RunMonkeyRunnable implements Runnable {
+
+        private String packages;
+        private final static int ITERATIONS = 1000;
+
+        public RunMonkeyRunnable(String packages) {
+            this.packages = packages;
+        }
+
+        @Override
+        public void run() {
+            Main.getUI().showWaitDialog();
+
+            try {
+                String pkgs[] = packages.split(",");
+
+                for (String pkg : pkgs) {
+                    Main.getUI().updateWaitDialog("Running monkey on " + pkg);
+
+                    try {
+                        // Stop running app.
+                        forceStop(pkg);
+
+                        // Little bit of breather here.
+                        try {
+                            Thread.sleep(1000);
+                        } catch (Exception e) {
+                        }
+
+                        DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1,
+                                TimeUnit.MINUTES);
+
+                        Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
+                        Map<String, String> data = Main.findAndGetClassData(device, pkg);
+                        DumpData dumpData = new DumpData(pkg, data, new Date());
+                        dataTableModel.addData(dumpData);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    } finally {
+                        // Stop running app.
+                        forceStop(pkg);
+                    }
+                }
+            } finally {
+                Main.getUI().hideWaitDialog();
+            }
+        }
+
+        private void forceStop(String packageName) {
+            // Stop running app.
+            DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS);
+            DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS);
+            DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
new file mode 100644
index 0000000..d74b8a3
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.preload.ClientUtils;
+import com.android.preload.DumpData;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.util.Date;
+import java.util.Map;
+
+public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction {
+
+    private ClientUtils clientUtils;
+    private DumpTableModel dataTableModel;
+
+    public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
+        super("Scan all packages", device);
+        this.clientUtils = utils;
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void run() {
+        Main.getUI().showWaitDialog();
+
+        try {
+            Client[] clients = clientUtils.findAllClients(device);
+            for (Client c : clients) {
+                String pkg = c.getClientData().getClientDescription();
+                Main.getUI().showWaitDialog();
+                Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
+
+                try {
+                    Map<String, String> data = Main.getClassDataRetriever().getClassData(c);
+                    DumpData dumpData = new DumpData(pkg, data, new Date());
+                    dataTableModel.addData(dumpData);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        } finally {
+            Main.getUI().hideWaitDialog();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
new file mode 100644
index 0000000..98492bd
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.preload.ClientUtils;
+import com.android.preload.DumpData;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.util.Date;
+import java.util.Map;
+
+public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction {
+
+    private ClientUtils clientUtils;
+    private DumpTableModel dataTableModel;
+
+    public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
+        super("Scan package", device);
+        this.clientUtils = utils;
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void run() {
+        Main.getUI().showWaitDialog();
+
+        try {
+            Client client = Main.getUI().getSelectedClient();
+            if (client != null) {
+                work(client);
+            } else {
+                Client[] clients = clientUtils.findAllClients(device);
+                if (clients.length > 0) {
+                    ClientWrapper[] clientWrappers = new ClientWrapper[clients.length];
+                    for (int i = 0; i < clientWrappers.length; i++) {
+                        clientWrappers[i] = new ClientWrapper(clients[i]);
+                    }
+                    Main.getUI().hideWaitDialog();
+
+                    ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan",
+                            "Choose package",
+                            clientWrappers);
+                    if (ret != null) {
+                        work(ret.client);
+                    }
+                }
+            }
+        } finally {
+            Main.getUI().hideWaitDialog();
+        }
+    }
+
+    private void work(Client c) {
+        String pkg = c.getClientData().getClientDescription();
+        Main.getUI().showWaitDialog();
+        Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
+
+        try {
+            Map<String, String> data = Main.findAndGetClassData(device, pkg);
+            DumpData dumpData = new DumpData(pkg, data, new Date());
+            dataTableModel.addData(dumpData);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static class ClientWrapper {
+        private Client client;
+
+        public ClientWrapper(Client c) {
+            client = c;
+        }
+
+        @Override
+        public String toString() {
+            return client.getClientData().getClientDescription() + " (pid "
+                    + client.getClientData().getPid() + ")";
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
new file mode 100644
index 0000000..2bb175f
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 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.preload.actions;
+
+import com.android.preload.DumpData;
+import com.android.preload.DumpTableModel;
+import com.android.preload.Main;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.JFrame;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+public class ShowDataAction extends AbstractAction {
+    private DumpTableModel dataTableModel;
+
+    public ShowDataAction(DumpTableModel dataTableModel) {
+        super("Show data");
+        this.dataTableModel = dataTableModel;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        // TODO(agampe): Auto-generated method stub
+        int selRow = Main.getUI().getSelectedDataTableRow();
+        if (selRow != -1) {
+            DumpData data = dataTableModel.getData().get(selRow);
+            Map<String, Set<String>> inv = data.invertData();
+
+            StringBuilder builder = new StringBuilder();
+
+            // First bootclasspath.
+            add(builder, "Boot classpath:", inv.get(null));
+
+            // Now everything else.
+            for (String k : inv.keySet()) {
+                if (k != null) {
+                    builder.append("==================\n\n");
+                    add(builder, k, inv.get(k));
+                }
+            }
+
+            JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate());
+            newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
+            newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())),
+                    BorderLayout.CENTER);
+            newFrame.setSize(800, 600);
+            newFrame.setLocationRelativeTo(null);
+            newFrame.setVisible(true);
+        }
+    }
+
+    private void add(StringBuilder builder, String head, Set<String> set) {
+        builder.append(head);
+        builder.append('\n');
+        addSet(builder, set);
+        builder.append('\n');
+    }
+
+    private void addSet(StringBuilder builder, Set<String> set) {
+        if (set == null) {
+            builder.append("  NONE\n");
+            return;
+        }
+        List<String> sorted = new ArrayList<>(set);
+        Collections.sort(sorted);
+        for (String s : sorted) {
+            builder.append(s);
+            builder.append('\n');
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java b/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
similarity index 62%
copy from tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java
copy to tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
index a9e1777..f04360f 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java
+++ b/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.perftest;
-import android.renderscript.*;
-import android.content.res.Resources;
+package com.android.preload.classdataretrieval;
 
-interface RsBenchBaseTest {
-    boolean init(RenderScriptGL rs, Resources res);
+import com.android.ddmlib.Client;
 
-    ScriptField_TestScripts_s.Item[] getTests();
-    String[] getTestNames();
+import java.util.Map;
+
+/**
+ * Retrieve a class-to-classloader map for loaded classes from the client.
+ */
+public interface ClassDataRetriever {
+
+    public Map<String, String> getClassData(Client client);
 }
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
new file mode 100644
index 0000000..8d797ee
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.preload.classdataretrieval.hprof;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData.IHprofDumpHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GeneralHprofDumpHandler implements IHprofDumpHandler {
+
+    private List<IHprofDumpHandler> handlers = new ArrayList<>();
+
+    public void addHandler(IHprofDumpHandler h) {
+      synchronized (handlers) {
+        handlers.add(h);
+      }
+    }
+
+    public void removeHandler(IHprofDumpHandler h) {
+      synchronized (handlers) {
+        handlers.remove(h);
+      }
+    }
+
+    private List<IHprofDumpHandler> getIterationList() {
+      synchronized (handlers) {
+        return new ArrayList<>(handlers);
+      }
+    }
+
+    @Override
+    public void onEndFailure(Client arg0, String arg1) {
+      List<IHprofDumpHandler> iterList = getIterationList();
+      for (IHprofDumpHandler h : iterList) {
+        h.onEndFailure(arg0, arg1);
+      }
+    }
+
+    @Override
+    public void onSuccess(String arg0, Client arg1) {
+      List<IHprofDumpHandler> iterList = getIterationList();
+      for (IHprofDumpHandler h : iterList) {
+        h.onSuccess(arg0, arg1);
+      }
+    }
+
+    @Override
+    public void onSuccess(byte[] arg0, Client arg1) {
+      List<IHprofDumpHandler> iterList = getIterationList();
+      for (IHprofDumpHandler h : iterList) {
+        h.onSuccess(arg0, arg1);
+      }
+    }
+  }
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
new file mode 100644
index 0000000..21b7a04
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015 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.preload.classdataretrieval.hprof;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+import com.android.ddmlib.ClientData.IHprofDumpHandler;
+import com.android.preload.classdataretrieval.ClassDataRetriever;
+import com.android.preload.ui.NullProgressMonitor;
+import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
+import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Queries;
+import com.android.tools.perflib.heap.Snapshot;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class Hprof implements ClassDataRetriever {
+
+    private static GeneralHprofDumpHandler hprofHandler;
+
+    public static void init() {
+        synchronized(Hprof.class) {
+            if (hprofHandler == null) {
+                ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler());
+            }
+        }
+    }
+
+    public static File doHprof(Client client, int timeout) {
+        GetHprof gh = new GetHprof(client, timeout);
+        return gh.get();
+    }
+
+    /**
+     * Return a map of class names to class-loader names derived from the hprof dump.
+     *
+     * @param hprofLocalFile
+     */
+    public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception {
+        Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile));
+
+        Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null);
+        Map<String, String> retValue = new HashMap<String, String>();
+        for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) {
+            for (ClassObj c : e.getValue()) {
+                String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString();
+                String cName = c.getClassName();
+                int aDepth = 0;
+                while (cName.endsWith("[]")) {
+                    cName = cName.substring(0, cName.length()-2);
+                    aDepth++;
+                }
+                String newName = transformPrimitiveClass(cName);
+                if (aDepth > 0) {
+                    // Need to use kind-a descriptor syntax. If it was transformed, it is primitive.
+                    if (newName.equals(cName)) {
+                        newName = "L" + newName + ";";
+                    }
+                    for (int i = 0; i < aDepth; i++) {
+                        newName = "[" + newName;
+                    }
+                }
+                retValue.put(newName, cl);
+            }
+        }
+
+        // Free up memory.
+        snapshot.dispose();
+
+        return retValue;
+    }
+
+    private static Map<String, String> primitiveMapping;
+
+    static {
+        primitiveMapping = new HashMap<>();
+        primitiveMapping.put("boolean", "Z");
+        primitiveMapping.put("byte", "B");
+        primitiveMapping.put("char", "C");
+        primitiveMapping.put("double", "D");
+        primitiveMapping.put("float", "F");
+        primitiveMapping.put("int", "I");
+        primitiveMapping.put("long", "J");
+        primitiveMapping.put("short", "S");
+        primitiveMapping.put("void", "V");
+    }
+
+    private static String transformPrimitiveClass(String name) {
+        String rep = primitiveMapping.get(name);
+        if (rep != null) {
+            return rep;
+        }
+        return name;
+    }
+
+    private static class GetHprof implements IHprofDumpHandler {
+
+        private File target;
+        private long timeout;
+        private Client client;
+
+        public GetHprof(Client client, long timeout) {
+            this.client = client;
+            this.timeout = timeout;
+        }
+
+        public File get() {
+            synchronized (this) {
+                hprofHandler.addHandler(this);
+                client.dumpHprof();
+                if (target == null) {
+                    try {
+                        wait(timeout);
+                    } catch (Exception e) {
+                        System.out.println(e);
+                    }
+                }
+            }
+
+            hprofHandler.removeHandler(this);
+            return target;
+        }
+
+        private void wakeUp() {
+            synchronized (this) {
+                notifyAll();
+            }
+        }
+
+        @Override
+        public void onEndFailure(Client arg0, String arg1) {
+            System.out.println("GetHprof.onEndFailure");
+            if (client == arg0) {
+                wakeUp();
+            }
+        }
+
+        private static File createTargetFile() {
+            try {
+                return File.createTempFile("ddms", ".hprof");
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void onSuccess(String arg0, Client arg1) {
+            System.out.println("GetHprof.onSuccess");
+            if (client == arg1) {
+                try {
+                    target = createTargetFile();
+                    arg1.getDevice().getSyncService().pullFile(arg0,
+                            target.getAbsoluteFile().toString(), new NullProgressMonitor());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    target = null;
+                }
+                wakeUp();
+            }
+        }
+
+        @Override
+        public void onSuccess(byte[] arg0, Client arg1) {
+            System.out.println("GetHprof.onSuccess");
+            if (client == arg1) {
+                try {
+                    target = createTargetFile();
+                    BufferedOutputStream out =
+                            new BufferedOutputStream(new FileOutputStream(target));
+                    out.write(arg0);
+                    out.close();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    target = null;
+                }
+                wakeUp();
+            }
+        }
+    }
+
+    private int timeout;
+
+    public Hprof(int timeout) {
+        this.timeout = timeout;
+    }
+
+    @Override
+    public Map<String, String> getClassData(Client client) {
+        File hprofLocalFile = Hprof.doHprof(client, timeout);
+        if (hprofLocalFile == null) {
+            throw new RuntimeException("Failed getting dump...");
+        }
+        System.out.println("Dump file is " + hprofLocalFile);
+
+        try {
+            return analyzeHprof(hprofLocalFile);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
new file mode 100644
index 0000000..dbd4c89
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2015 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.preload.classdataretrieval.jdwp;
+
+import com.android.ddmlib.Client;
+import com.android.preload.classdataretrieval.ClassDataRetriever;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
+import org.apache.harmony.jpda.tests.share.JPDALogWriter;
+import org.apache.harmony.jpda.tests.share.JPDATestOptions;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
+
+    private final Client client;
+
+    public JDWPClassDataRetriever() {
+        this(null);
+    }
+
+    public JDWPClassDataRetriever(Client client) {
+        this.client = client;
+    }
+
+
+    @Override
+    protected String getDebuggeeClassName() {
+        return "<unset>";
+    }
+
+    @Override
+    public Map<String, String> getClassData(Client client) {
+        return new JDWPClassDataRetriever(client).retrieve();
+    }
+
+    private Map<String, String> retrieve() {
+        if (client == null) {
+            throw new IllegalStateException();
+        }
+
+        settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
+        settings.setDebuggeeSuspend("n");
+
+        logWriter = new JPDALogWriter(System.out, "", false);
+
+        try {
+            internalSetUp();
+
+            return retrieveImpl();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        } finally {
+            internalTearDown();
+        }
+    }
+
+    private Map<String, String> retrieveImpl() {
+        try {
+            // Suspend the app.
+            {
+                CommandPacket packet = new CommandPacket(
+                        JDWPCommands.VirtualMachineCommandSet.CommandSetID,
+                        JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
+                ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
+                if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
+                    return null;
+                }
+            }
+
+            // List all classes.
+            CommandPacket packet = new CommandPacket(
+                    JDWPCommands.VirtualMachineCommandSet.CommandSetID,
+                    JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
+            ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
+
+            if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
+                return null;
+            }
+
+            int classCount = reply.getNextValueAsInt();
+            System.out.println("Runtime reported " + classCount + " classes.");
+
+            Map<Long, String> classes = new HashMap<Long, String>();
+            Map<Long, String> arrayClasses = new HashMap<Long, String>();
+
+            for (int i = 0; i < classCount; i++) {
+                byte refTypeTag = reply.getNextValueAsByte();
+                long typeID = reply.getNextValueAsReferenceTypeID();
+                String signature = reply.getNextValueAsString();
+                /* int status = */ reply.getNextValueAsInt();
+
+                switch (refTypeTag) {
+                    case JDWPConstants.TypeTag.CLASS:
+                    case JDWPConstants.TypeTag.INTERFACE:
+                        classes.put(typeID, signature);
+                        break;
+
+                    case JDWPConstants.TypeTag.ARRAY:
+                        arrayClasses.put(typeID, signature);
+                        break;
+                }
+            }
+
+            Map<String, String> result = new HashMap<String, String>();
+
+            // Parse all classes.
+            for (Map.Entry<Long, String> entry : classes.entrySet()) {
+                long typeID = entry.getKey();
+                String signature = entry.getValue();
+
+                if (!checkClass(typeID, signature, result)) {
+                    System.err.println("Issue investigating " + signature);
+                }
+            }
+
+            // For arrays, look at the leaf component type.
+            for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
+                long typeID = entry.getKey();
+                String signature = entry.getValue();
+
+                if (!checkArrayClass(typeID, signature, result)) {
+                    System.err.println("Issue investigating " + signature);
+                }
+            }
+
+            return result;
+        } finally {
+            // Resume the app.
+            {
+                CommandPacket packet = new CommandPacket(
+                        JDWPCommands.VirtualMachineCommandSet.CommandSetID,
+                        JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
+                /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
+            }
+        }
+    }
+
+    private boolean checkClass(long typeID, String signature, Map<String, String> result) {
+        CommandPacket packet = new CommandPacket(
+                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
+                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
+        packet.setNextValueAsReferenceTypeID(typeID);
+        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
+        if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
+            return false;
+        }
+
+        long classLoaderID = reply.getNextValueAsObjectID();
+
+        // TODO: Investigate the classloader to have a better string?
+        String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
+
+        result.put(getClassName(signature), classLoaderString);
+
+        return true;
+    }
+
+    private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
+        // Classloaders of array classes are the same as the component class'.
+        CommandPacket packet = new CommandPacket(
+                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
+                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
+        packet.setNextValueAsReferenceTypeID(typeID);
+        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
+        if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
+            return false;
+        }
+
+        long classLoaderID = reply.getNextValueAsObjectID();
+
+        // TODO: Investigate the classloader to have a better string?
+        String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
+
+        // For array classes, we *need* the signature directly.
+        result.put(signature, classLoaderString);
+
+        return true;
+    }
+
+    private static String getClassName(String signature) {
+        String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
+        return withoutLAndSemicolon.replace('/', '.');
+    }
+
+
+    private static JPDATestOptions createTestOptions(String address) {
+        JPDATestOptions options = new JPDATestOptions();
+        options.setAttachConnectorKind();
+        options.setTimeout(1000);
+        options.setWaitingTime(1000);
+        options.setTransportAddress(address);
+        return options;
+    }
+
+    @Override
+    protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
+        return new PreloadDebugeeWrapper(settings, logWriter);
+    }
+}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
new file mode 100644
index 0000000..b9df6d0
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.preload.classdataretrieval.jdwp;
+
+import org.apache.harmony.jpda.tests.framework.LogWriter;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper;
+import org.apache.harmony.jpda.tests.share.JPDATestOptions;
+
+import java.io.IOException;
+
+public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper {
+
+    public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) {
+        super(options, writer);
+    }
+
+    @Override
+    protected Process launchProcess(String cmdLine) throws IOException {
+        return null;
+    }
+
+    @Override
+    protected void WaitForProcessExit(Process process) {
+    }
+
+}
diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
new file mode 100644
index 0000000..f45aad0
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.preload.ui;
+
+import com.android.ddmlib.SyncService.ISyncProgressMonitor;
+
+public class NullProgressMonitor implements ISyncProgressMonitor {
+
+    @Override
+    public void advance(int arg0) {}
+
+    @Override
+    public boolean isCanceled() {
+        return false;
+    }
+
+    @Override
+    public void start(int arg0) {}
+
+    @Override
+    public void startSubTask(String arg0) {}
+
+    @Override
+    public void stop() {}
+}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/ui/UI.java b/tools/preload2/src/com/android/preload/ui/UI.java
new file mode 100644
index 0000000..47174dd
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ui/UI.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2015 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.preload.ui;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.io.File;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JProgressBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JToolBar;
+import javax.swing.ListModel;
+import javax.swing.SwingUtilities;
+import javax.swing.table.TableModel;
+
+public class UI extends JFrame {
+
+    private JList<Client> clientList;
+    private JTable dataTable;
+
+    // Shared file chooser, means the directory is retained.
+    private JFileChooser jfc;
+
+    public UI(ListModel<Client> clientListModel,
+              TableModel dataTableModel,
+              List<Action> actions) {
+        super("Preloaded-classes computation");
+
+        getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
+                BorderLayout.WEST);
+        clientList.setCellRenderer(new ClientListCellRenderer());
+        // clientList.addListSelectionListener(listener);
+
+        dataTable = new JTable(dataTableModel);
+        getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER);
+
+        JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
+        for (Action a : actions) {
+            if (a == null) {
+                toolbar.addSeparator();
+            } else {
+                toolbar.add(a);
+            }
+        }
+        getContentPane().add(toolbar, BorderLayout.PAGE_START);
+
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setBounds(100, 100, 800, 600);
+    }
+
+    public Client getSelectedClient() {
+        return clientList.getSelectedValue();
+    }
+
+    public int getSelectedDataTableRow() {
+        return dataTable.getSelectedRow();
+    }
+
+    private JDialog currentWaitDialog = null;
+
+    public void showWaitDialog() {
+        if (currentWaitDialog == null) {
+            currentWaitDialog = new JDialog(this, "Please wait...", true);
+            currentWaitDialog.getContentPane().add(new JLabel("Please be patient."),
+                    BorderLayout.CENTER);
+            JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL);
+            progress.setIndeterminate(true);
+            currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH);
+            currentWaitDialog.setSize(200, 100);
+            currentWaitDialog.setLocationRelativeTo(null);
+            showWaitDialogLater();
+        }
+    }
+
+    private void showWaitDialogLater() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                if (currentWaitDialog != null) {
+                    currentWaitDialog.setVisible(true); // This is blocking.
+                }
+            }
+        });
+    }
+
+    public void updateWaitDialog(String s) {
+        if (currentWaitDialog != null) {
+            ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
+            Dimension prefSize = currentWaitDialog.getPreferredSize();
+            Dimension curSize = currentWaitDialog.getSize();
+            if (prefSize.width > curSize.width || prefSize.height > curSize.height) {
+                currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width),
+                        Math.max(prefSize.height, curSize.height));
+                currentWaitDialog.invalidate();
+            }
+        }
+    }
+
+    public void hideWaitDialog() {
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+            currentWaitDialog = null;
+        }
+    }
+
+    public void showMessageDialog(String s) {
+        // Hide the wait dialog...
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+        }
+
+        try {
+            JOptionPane.showMessageDialog(this, s);
+        } finally {
+            // And reshow it afterwards...
+            if (currentWaitDialog != null) {
+                showWaitDialogLater();
+            }
+        }
+    }
+
+    public boolean showConfirmDialog(String title, String message) {
+        // Hide the wait dialog...
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+        }
+
+        try {
+            return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION)
+                    == JOptionPane.YES_OPTION;
+        } finally {
+            // And reshow it afterwards...
+            if (currentWaitDialog != null) {
+                showWaitDialogLater();
+            }
+        }
+    }
+
+    public String showInputDialog(String message) {
+        // Hide the wait dialog...
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+        }
+
+        try {
+            return JOptionPane.showInputDialog(message);
+        } finally {
+            // And reshow it afterwards...
+            if (currentWaitDialog != null) {
+                showWaitDialogLater();
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T showChoiceDialog(String title, String message, T[] choices) {
+        // Hide the wait dialog...
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+        }
+
+        try{
+            return (T)JOptionPane.showInputDialog(this,
+                    title,
+                    message,
+                    JOptionPane.QUESTION_MESSAGE,
+                    null,
+                    choices,
+                    choices[0]);
+        } finally {
+            // And reshow it afterwards...
+            if (currentWaitDialog != null) {
+                showWaitDialogLater();
+            }
+        }
+    }
+
+    public File showSaveDialog() {
+        // Hide the wait dialog...
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+        }
+
+        try{
+            if (jfc == null) {
+                jfc = new JFileChooser();
+            }
+
+            int ret = jfc.showSaveDialog(this);
+            if (ret == JFileChooser.APPROVE_OPTION) {
+                return jfc.getSelectedFile();
+            } else {
+                return null;
+            }
+        } finally {
+            // And reshow it afterwards...
+            if (currentWaitDialog != null) {
+                showWaitDialogLater();
+            }
+        }
+    }
+
+    public File[] showOpenDialog(boolean multi) {
+        // Hide the wait dialog...
+        if (currentWaitDialog != null) {
+            currentWaitDialog.setVisible(false);
+        }
+
+        try{
+            if (jfc == null) {
+                jfc = new JFileChooser();
+            }
+
+            jfc.setMultiSelectionEnabled(multi);
+            int ret = jfc.showOpenDialog(this);
+            if (ret == JFileChooser.APPROVE_OPTION) {
+                return jfc.getSelectedFiles();
+            } else {
+                return null;
+            }
+        } finally {
+            // And reshow it afterwards...
+            if (currentWaitDialog != null) {
+                showWaitDialogLater();
+            }
+        }
+    }
+
+    private class ClientListCellRenderer extends DefaultListCellRenderer {
+
+        @Override
+        public Component getListCellRendererComponent(JList<?> list, Object value, int index,
+                boolean isSelected, boolean cellHasFocus) {
+            ClientData cd = ((Client) value).getClientData();
+            String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")";
+            return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index dc329e2..0c06ae8 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -24,15 +24,12 @@
 import android.net.StaticIpConfiguration;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.Log;
 
-import java.util.HashMap;
+import java.util.Arrays;
 import java.util.BitSet;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
+import java.util.HashMap;
 
 /**
  * A class representing a configured Wi-Fi network, including the
@@ -182,19 +179,6 @@
     }
 
     /** @hide */
-    public static final int DISABLED_UNKNOWN_REASON                         = 0;
-    /** @hide */
-    public static final int DISABLED_DNS_FAILURE                            = 1;
-    /** @hide */
-    public static final int DISABLED_DHCP_FAILURE                           = 2;
-    /** @hide */
-    public static final int DISABLED_AUTH_FAILURE                           = 3;
-    /** @hide */
-    public static final int DISABLED_ASSOCIATION_REJECT                     = 4;
-    /** @hide */
-    public static final int DISABLED_BY_WIFI_MANAGER                        = 5;
-
-    /** @hide */
     public static final int UNKNOWN_UID = -1;
 
     /**
@@ -206,24 +190,12 @@
 
     /**
      * The current status of this network configuration entry.
+     * Fixme We need remove this field to use only Quality network selection status only
      * @see Status
      */
     public int status;
 
     /**
-     * The configuration needs to be written to networkHistory.txt
-     * @hide
-     */
-    public boolean dirty;
-
-    /**
-     * The code referring to a reason for disabling the network
-     * Valid when {@link #status} == Status.DISABLED
-     * @hide
-     */
-    public int disableReason;
-
-    /**
      * The network's SSID. Can either be an ASCII string,
      * which must be enclosed in double quotation marks
      * (e.g., {@code "MyNetwork"}, or a string of
@@ -356,6 +328,13 @@
 
     /**
      * @hide
+     * This network configuration is visible to and usable by other users on the
+     * same device.
+     */
+    public boolean shared;
+
+    /**
+     * @hide
      */
     private IpConfiguration mIpConfiguration;
 
@@ -421,12 +400,6 @@
 
     /**
      * @hide
-     * Uid used by autoJoin
-     */
-    public String autoJoinBSSID;
-
-    /**
-     * @hide
      * Status of user approval for connection
      */
     public int userApproved = USER_UNSPECIFIED;
@@ -444,77 +417,6 @@
     /** @hide **/
     public static int INVALID_RSSI = -127;
 
-    /** @hide **/
-    public static int UNWANTED_BLACKLIST_SOFT_RSSI_24 = -80;
-
-    /** @hide **/
-    public static int UNWANTED_BLACKLIST_SOFT_RSSI_5 = -70;
-
-    /** @hide **/
-    public static int GOOD_RSSI_24 = -65;
-
-    /** @hide **/
-    public static int LOW_RSSI_24 = -77;
-
-    /** @hide **/
-    public static int BAD_RSSI_24 = -87;
-
-    /** @hide **/
-    public static int GOOD_RSSI_5 = -60;
-
-    /** @hide **/
-    public static int LOW_RSSI_5 = -72;
-
-    /** @hide **/
-    public static int BAD_RSSI_5 = -82;
-
-    /** @hide **/
-    public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
-
-    /** @hide **/
-    public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
-
-    /** @hide **/
-    public static int UNBLACKLIST_THRESHOLD_24_SOFT = -77;
-
-    /** @hide **/
-    public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
-
-    /** @hide **/
-    public static int UNBLACKLIST_THRESHOLD_5_SOFT = -63;
-
-    /** @hide **/
-    public static int UNBLACKLIST_THRESHOLD_5_HARD = -56;
-
-    /** @hide **/
-    public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_24 = -80;
-
-    /** @hide **/
-    public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70;
-
-    /** @hide
-     * 5GHz band is prefered low over 2.4 if the 5GHz RSSI is higher than this threshold */
-    public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
-
-    /** @hide
-     * 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
-    public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
-
-    /** @hide
-     * Boost given to RSSI on a home network for the purpose of calculating the score
-     * This adds stickiness to home networks, as defined by:
-     * - less than 4 known BSSIDs
-     * - PSK only
-     * - TODO: add a test to verify that all BSSIDs are behind same gateway
-     ***/
-    public static int HOME_NETWORK_RSSI_BOOST = 5;
-
-    /** @hide
-     * RSSI boost for configuration which use autoJoinUseAggressiveJoinAttemptThreshold
-     * To be more aggressive when initially attempting to auto join
-     */
-    public static int MAX_INITIAL_AUTO_JOIN_RSSI_BOOST = 8;
-
     /**
      * @hide
      * A summary of the RSSI and Band status for that configuration
@@ -600,38 +502,6 @@
         visibility = status;
     }
 
-    /** @hide */
-    public static final int AUTO_JOIN_ENABLED                   = 0;
-    /**
-     * if this is set, the WifiConfiguration cannot use linkages so as to bump
-     * it's relative priority.
-     * - status between and 128 indicate various level of blacklisting depending
-     * on the severity or frequency of the connection error
-     * - deleted status indicates that the user is deleting the configuration, and so
-     * although it may have been self added we will not re-self-add it, ignore it,
-     * not return it to applications, and not connect to it
-     * */
-
-    /** @hide
-     * network was temporary disabled due to bad connection, most likely due
-     * to weak RSSI */
-    public static final int AUTO_JOIN_TEMPORARY_DISABLED  = 1;
-    /** @hide
-     * network was temporary disabled due to bad connection, which cant be attributed
-     * to weak RSSI */
-    public static final int AUTO_JOIN_TEMPORARY_DISABLED_LINK_ERRORS  = 32;
-    /** @hide */
-    public static final int AUTO_JOIN_TEMPORARY_DISABLED_AT_SUPPLICANT  = 64;
-    /** @hide */
-    public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE  = 128;
-    /** @hide */
-    public static final int AUTO_JOIN_DISABLED_NO_CREDENTIALS = 160;
-    /** @hide */
-    public static final int AUTO_JOIN_DISABLED_USER_ACTION = 161;
-
-    /** @hide */
-    public static final int AUTO_JOIN_DELETED  = 200;
-
     // States for the userApproved field
     /**
      * @hide
@@ -656,29 +526,6 @@
 
     /**
      * @hide
-     */
-    public int autoJoinStatus;
-
-    /**
-     * @hide
-     * Number of connection failures
-     */
-    public int numConnectionFailures;
-
-    /**
-     * @hide
-     * Number of IP config failures
-     */
-    public int numIpConfigFailures;
-
-    /**
-     * @hide
-     * Number of Auth failures
-     */
-    public int numAuthFailures;
-
-    /**
-     * @hide
      * Number of reports indicating no Internet Access
      */
     public int numNoInternetAccessReports;
@@ -715,12 +562,6 @@
 
     /**
      * @hide
-     * Last time we blacklisted the configuration
-     */
-    public long blackListTimestamp;
-
-    /**
-     * @hide
      * Last time the system was connected to this configuration.
      */
     public long lastConnected;
@@ -795,18 +636,6 @@
 
     /**
      * @hide
-     * Indicate that we didn't auto-join because rssi was too low
-     */
-    public boolean autoJoinBailedDueToLowRssi;
-
-    /**
-     * @hide
-     * AutoJoin even though RSSI is 10dB below threshold
-     */
-    public int autoJoinUseAggressiveJoinAttemptThreshold;
-
-    /**
-     * @hide
      * Number of time the scorer overrode a the priority based choice, when comparing two
      * WifiConfigurations, note that since comparing WifiConfiguration happens very often
      * potentially at every scan, this number might become very large, even on an idle
@@ -892,6 +721,361 @@
      */
     public HashMap<String, Integer> connectChoices;
 
+    /** @hide
+     * Boost given to RSSI on a home network for the purpose of calculating the score
+     * This adds stickiness to home networks, as defined by:
+     * - less than 4 known BSSIDs
+     * - PSK only
+     * - TODO: add a test to verify that all BSSIDs are behind same gateway
+     ***/
+    public static final int HOME_NETWORK_RSSI_BOOST = 5;
+
+    /**
+     * @hide
+     * This class is used to contain all the information and API used for quality network selection
+     */
+    public static class NetworkSelectionStatus {
+        /**
+         * Quality Network Selection Status enable, temporary disabled, permanently disabled
+         */
+        /**
+         * This network is allowed to join Quality Network Selection
+         */
+        public static final int NETWORK_SELECTION_ENABLED = 0;
+        /**
+         * network was temporary disabled. Can be re-enabled after a time period expire
+         */
+        public static final int NETWORK_SELECTION_TEMPORARY_DISABLED  = 1;
+        /**
+         * network was permanently disabled.
+         */
+        public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED  = 2;
+        /**
+         * Maximum Network selection status
+         */
+        public static final int NETWORK_SELECTION_STATUS_MAX = 3;
+
+        /**
+         * Quality network selection status String (for debug purpose). Use Quality network
+         * selection status value as index to extec the corresponding debug string
+         */
+        private static final String[] QUALITY_NETWORK_SELECTION_STATUS = {
+                "NETWORK_SELECTION_ENABLED", "NETWORK_SELECTION_TEMPORARY_DISABLED",
+                "NETWORK_SELECTION_PERMANENTLY_DISABLED"};
+
+        //Quality Network disabled reasons
+        /**
+         * Default value. Means not disabled
+         */
+        public static final int NETWORK_SELECTION_ENABLE = 0;
+        /**
+         * This network is disabled because higher layer (>2) network is bad
+         */
+        public static final int DISABLED_BAD_LINK = 1;
+        /**
+         * This network is disabled because multiple association rejects
+         */
+        public static final int DISABLED_ASSOCIATION_REJECTION = 2;
+        /**
+         * This network is disabled because multiple authentication failure
+         */
+        public static final int DISABLED_AUTHENTICATION_FAILURE = 3;
+        /**
+         * This network is disabled because multiple DHCP failure
+         */
+        public static final int DISABLED_DHCP_FAILURE = 4;
+        /**
+         * This network is disabled because of security network but no credentials
+         */
+        public static final int DISABLED_DNS_FAILURE = 5;
+        /**
+         * This network is disabled because EAP-TLS failure
+         */
+        public static final int DISABLED_TLS_VERSION_MISMATCH = 6;
+        /**
+         * This network is disabled due to WifiManager disable it explicitly
+         */
+        public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 7;
+        /**
+         * This network is disabled because no Internet connected and user do not want
+         */
+        public static final int DISABLED_NO_INTERNET = 8;
+        /**
+         * This network is disabled due to WifiManager disable it explicitly
+         */
+        public static final int DISABLED_BY_WIFI_MANAGER = 9;
+        /**
+         * This Maximum disable reason value
+         */
+        public static final int NETWORK_SELECTION_DISABLED_MAX = 10;
+
+        /**
+         * Quality network selection disable reason String (for debug purpose)
+         */
+        private static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
+                "NETWORK_SELECTION_ENABLE", "NETWORK_SELECTION_DISABLED_BAD_LINK",
+                "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ",
+                "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE",
+                "NETWORK_SELECTION_DISABLED_DHCP_FAILURE",
+                "NETWORK_SELECTION_DISABLED_DNS_FAILURE", "NETWORK_SELECTION_DISABLED_TLS_VERSION",
+                "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS",
+                "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER"};
+
+        /**
+         * Invalid time stamp for network selection disable
+         */
+        public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L;
+
+        // fields for QualityNetwork Selection
+        /**
+         * Network selection status, should be in one of three status: enable, temporaily disabled
+         * or permanently disabled
+         */
+        private int mStatus;
+
+        /**
+         * Reason for disable this network
+         */
+        private int mNetworkSelectionDisableReason;
+
+        /**
+         * Last time we temporarily disabled the configuration
+         */
+        private long mTemporarilyDisabledTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
+
+        /**
+         * counter for each Network selection disable reason
+         */
+        private int[] mNetworkSeclectionDisableCounter = new int[NETWORK_SELECTION_DISABLED_MAX];
+
+        /**
+         * return current Quality network selection status in String (for debug purpose)
+         */
+        public String getNetworkStatusString() {
+            return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
+        }
+
+        private NetworkSelectionStatus() {};
+
+        /**
+         * @param reason specific error reason
+         * @return  corresponding network disable reason String (for debug purpose)
+         */
+        public static String getNetworkDisableReasonString(int reason) {
+            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+                return QUALITY_NETWORK_SELECTION_DISABLE_REASON[reason];
+            } else {
+                return null;
+            }
+        }
+        /**
+         * @return current network disable reason in String (for debug purpose)
+         */
+        public String getNetworkDisableReasonString() {
+            return QUALITY_NETWORK_SELECTION_DISABLE_REASON[mNetworkSelectionDisableReason];
+        }
+
+        /**
+         * get current network network selection status
+         */
+        public int getNetworkSelectionStatus() {
+            return mStatus;
+        }
+        /**
+         * @return whether current network is enabled to join network selection
+         */
+        public boolean isNetworkEnabled() {
+            return mStatus == NETWORK_SELECTION_ENABLED;
+        }
+
+        /**
+         * @return whether current network is temporary disabled
+         */
+        public boolean isNetworkTemporaryDisabled() {
+            return mStatus == NETWORK_SELECTION_TEMPORARY_DISABLED;
+        }
+
+        /**
+         * return whether current network is permanently disabled
+         */
+        public boolean isNetworkPermanentlyDisabled() {
+            return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED;
+        }
+        /**
+         * @param status network selection status to set
+         */
+        public void setNetworkSelectionStatus(int status) {
+            if (status >= 0 && status < NETWORK_SELECTION_STATUS_MAX) {
+                mStatus = status;
+            }
+        }
+        /**
+         * @return current network's disable reason
+         */
+        public int getNetworkSelectionDisableReason() {
+            return mNetworkSelectionDisableReason;
+        }
+
+        /**
+         * @param  reason Network disable reason
+         */
+        public void setNetworkSelectionDisableReason(int reason) {
+            if (reason >= 0 && reason < NETWORK_SELECTION_DISABLED_MAX) {
+                mNetworkSelectionDisableReason = reason;
+            } else {
+                throw new IllegalArgumentException("Illegal reason value: " + reason);
+            }
+        }
+        /**
+         * @param reason whether current network is disabled by this reason
+         */
+        public boolean isDisabledByReason(int reason) {
+            return mNetworkSelectionDisableReason == reason;
+        }
+        /**
+         * @param timeStamp Set when current network is disabled in millisecond since January 1,
+         * 1970 00:00:00.0 UTC
+         */
+        public void setDisableTime(long timeStamp) {
+            mTemporarilyDisabledTimestamp = timeStamp;
+        }
+
+        /**
+         * @return Get when current network is disabled in millisecond since January 1,
+         * 1970 00:00:00.0 UTC
+         */
+        public long getDisableTime() {
+            return mTemporarilyDisabledTimestamp;
+        }
+
+        /**
+         * @param  reason specific failure reason
+         * @exception throw IllegalArgumentException for illegal input
+         * @return counter number for specific error reason.
+         */
+        public int getDisableReasonCounter(int reason) {
+            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+                return mNetworkSeclectionDisableCounter[reason];
+            } else {
+                throw new IllegalArgumentException("Illegal reason value: " + reason);
+            }
+        }
+
+        /**
+         * set the counter of a specific failure reason
+         * @param reason reason for disable error
+         * @param value the counter value for this specific reason
+         * @exception throw IllegalArgumentException for illegal input
+         */
+        public void setDisableReasonCounter(int reason, int value) {
+            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+                mNetworkSeclectionDisableCounter[reason] = value;
+            } else {
+                throw new IllegalArgumentException("Illegal reason value: " + reason);
+            }
+        }
+
+        /**
+         * increment the counter of a specific failure reason
+         * @param reason a specific failure reason
+         * @exception throw IllegalArgumentException for illegal input
+         */
+        public void incrementDisableReasonCounter(int reason) {
+            if (reason >= NETWORK_SELECTION_ENABLE  && reason < NETWORK_SELECTION_DISABLED_MAX) {
+                mNetworkSeclectionDisableCounter[reason]++;
+            } else {
+                throw new IllegalArgumentException("Illegal reason value: " + reason);
+            }
+        }
+        /**
+         * clear the counter of a specific failure reason
+         * @hide
+         * @param reason a specific failure reason
+         * @exception throw IllegalArgumentException for illegal input
+         */
+        public void clearDisableReasonCounter(int reason) {
+            if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+                mNetworkSeclectionDisableCounter[reason] = NETWORK_SELECTION_ENABLE;
+            } else {
+                throw new IllegalArgumentException("Illegal reason value: " + reason);
+            }
+        }
+        /**
+         * clear all the failure reason counters
+         */
+        public void clearDisableReasonCounter() {
+            Arrays.fill(mNetworkSeclectionDisableCounter, NETWORK_SELECTION_ENABLE);
+        }
+
+        /**
+         * BSSID for connection to this network (through network selection procedure)
+         */
+        private String mNetworkSelectionBSSID;
+
+        /**
+         * get current network Selection BSSID
+         * @return current network Selection BSSID
+         */
+        public String getNetworkSelectionBSSID() {
+            return mNetworkSelectionBSSID;
+        }
+
+        /**
+         * set network Selection BSSID
+         * @param bssid The target BSSID for assocaition
+         */
+        public void setNetworkSelectionBSSID(String bssid) {
+            mNetworkSelectionBSSID = bssid;
+        }
+
+        public void copy(NetworkSelectionStatus source) {
+            mStatus = source.mStatus;
+            mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason;
+            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+                    index++) {
+                mNetworkSeclectionDisableCounter[index] =
+                        source.mNetworkSeclectionDisableCounter[index];
+            }
+            mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
+            mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
+        }
+
+        public void writeToParcel(Parcel dest) {
+            dest.writeInt(getNetworkSelectionStatus());
+            dest.writeInt(getNetworkSelectionDisableReason());
+            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+                    index++) {
+                dest.writeInt(getDisableReasonCounter(index));
+            }
+            dest.writeLong(getDisableTime());
+            dest.writeString(getNetworkSelectionBSSID());
+        }
+
+        public void readFromParcel(Parcel in) {
+            setNetworkSelectionStatus(in.readInt());
+            setNetworkSelectionDisableReason(in.readInt());
+            for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+                    index++) {
+                setDisableReasonCounter(index, in.readInt());
+            }
+            setDisableTime(in.readLong());
+            setNetworkSelectionBSSID(in.readString());
+        }
+    }
+
+    /**
+     * @hide
+     * network selection related member
+     */
+    private final NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
+
+    /**
+     * @hide
+     * @return network selection status
+     */
+    public NetworkSelectionStatus getNetworkSelectionStatus() {
+        return mNetworkSelectionStatus;
+    }
     /**
      * @hide
      * Linked Configurations: represent the set of Wificonfigurations that are equivalent
@@ -910,7 +1094,6 @@
         roamingConsortiumIds = new long[0];
         priority = 0;
         hiddenSSID = false;
-        disableReason = DISABLED_UNKNOWN_REASON;
         allowedKeyManagement = new BitSet();
         allowedProtocols = new BitSet();
         allowedAuthAlgorithms = new BitSet();
@@ -921,7 +1104,6 @@
             wepKeys[i] = null;
         }
         enterpriseConfig = new WifiEnterpriseConfig();
-        autoJoinStatus = AUTO_JOIN_ENABLED;
         selfAdded = false;
         didSelfAdd = false;
         ephemeral = false;
@@ -929,6 +1111,7 @@
         mIpConfiguration = new IpConfiguration();
         lastUpdateUid = -1;
         creatorUid = -1;
+        shared = true;
     }
 
     /**
@@ -946,10 +1129,12 @@
      * @hide
      */
     public boolean isLinked(WifiConfiguration config) {
-        if (config.linkedConfigurations != null && linkedConfigurations != null) {
-            if (config.linkedConfigurations.get(configKey()) != null
-                    && linkedConfigurations.get(config.configKey()) != null) {
-                return true;
+        if (config != null) {
+            if (config.linkedConfigurations != null && linkedConfigurations != null) {
+                if (config.linkedConfigurations.get(configKey()) != null
+                        && linkedConfigurations.get(config.configKey()) != null) {
+                    return true;
+                }
             }
         }
         return  false;
@@ -964,20 +1149,6 @@
             allowedKeyManagement.get(KeyMgmt.IEEE8021X);
     }
 
-    /** @hide **/
-    public void setAutoJoinStatus(int status) {
-        if (status < 0) status = 0;
-        if (status == 0) {
-            blackListTimestamp = 0;
-        }  else if (status > autoJoinStatus) {
-            blackListTimestamp = System.currentTimeMillis();
-        }
-        if (status != autoJoinStatus) {
-            autoJoinStatus = status;
-            dirty = true;
-        }
-    }
-
     @Override
     public String toString() {
         StringBuilder sbuf = new StringBuilder();
@@ -988,24 +1159,28 @@
         }
         sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
                 append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
-                append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN).
-                append(" PRIO: ").append(this.priority).
-                append('\n');
-        if (this.numConnectionFailures > 0) {
-            sbuf.append(" numConnectFailures ").append(this.numConnectionFailures).append("\n");
+                append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
+                .append(" PRIO: ").append(this.priority)
+                .append('\n');
+
+
+        sbuf.append(" NetworkSelectionStatus ")
+                .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n");
+        if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) {
+            sbuf.append(" mNetworkSelectionDisableReason ")
+                    .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
+
+            for (int index = mNetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+                    index < mNetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
+                if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
+                    sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
+                            + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
+                            + "\n");
+                }
+            }
         }
-        if (this.numIpConfigFailures > 0) {
-            sbuf.append(" numIpConfigFailures ").append(this.numIpConfigFailures).append("\n");
-        }
-        if (this.numAuthFailures > 0) {
-            sbuf.append(" numAuthFailures ").append(this.numAuthFailures).append("\n");
-        }
-        if (this.autoJoinStatus > 0) {
-            sbuf.append(" autoJoinStatus ").append(this.autoJoinStatus).append("\n");
-        }
-        if (this.disableReason > 0) {
-            sbuf.append(" disableReason ").append(this.disableReason).append("\n");
-        }
+
+
         if (this.numAssociation > 0) {
             sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
         }
@@ -1094,11 +1269,15 @@
         sbuf.append("IP config:\n");
         sbuf.append(mIpConfiguration.toString());
 
-        if (this.autoJoinBSSID != null) sbuf.append(" autoJoinBSSID=" + autoJoinBSSID);
+        if (mNetworkSelectionStatus.getNetworkSelectionBSSID() != null) {
+            sbuf.append(" networkSelectionBSSID="
+                    + mNetworkSelectionStatus.getNetworkSelectionBSSID());
+        }
         long now_ms = System.currentTimeMillis();
-        if (this.blackListTimestamp != 0) {
+        if (mNetworkSelectionStatus.getDisableTime() != NetworkSelectionStatus
+                .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP) {
             sbuf.append('\n');
-            long diff = now_ms - this.blackListTimestamp;
+            long diff = now_ms - mNetworkSelectionStatus.getDisableTime();
             if (diff <= 0) {
                 sbuf.append(" blackListed since <incorrect>");
             } else {
@@ -1172,11 +1351,6 @@
         sbuf.append('\n');
         sbuf.append("triggeredJoin: ").append(this.numUserTriggeredJoinAttempts);
         sbuf.append('\n');
-        sbuf.append("autoJoinBailedDueToLowRssi: ").append(this.autoJoinBailedDueToLowRssi);
-        sbuf.append('\n');
-        sbuf.append("autoJoinUseAggressiveJoinAttemptThreshold: ");
-        sbuf.append(this.autoJoinUseAggressiveJoinAttemptThreshold);
-        sbuf.append('\n');
 
         return sbuf.toString();
     }
@@ -1323,6 +1497,9 @@
             key = mCachedConfigKey;
         } else if (providerFriendlyName != null) {
             key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+            if (!shared) {
+                key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
+            }
         } else {
             if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
                 key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
@@ -1334,6 +1511,9 @@
             } else {
                 key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
             }
+            if (!shared) {
+                key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
+            }
             mCachedConfigKey = key;
         }
         return key;
@@ -1346,27 +1526,6 @@
         return configKey(false);
     }
 
-    /** @hide
-     * return the config key string based on a scan result
-     */
-    static public String configKey(ScanResult result) {
-        String key = "\"" + result.SSID + "\"";
-
-        if (result.capabilities.contains("WEP")) {
-            key = key + "-WEP";
-        }
-
-        if (result.capabilities.contains("PSK")) {
-            key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
-        }
-
-        if (result.capabilities.contains("EAP")) {
-            key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
-        }
-
-        return key;
-    }
-
     /** @hide */
     public IpConfiguration getIpConfiguration() {
         return mIpConfiguration;
@@ -1428,13 +1587,16 @@
         return 0;
     }
 
+    /** @hide */
+    public boolean isVisibleToUser(int userId) {
+        return shared || (UserHandle.getUserId(creatorUid) == userId);
+    }
+
     /** copy constructor {@hide} */
     public WifiConfiguration(WifiConfiguration source) {
         if (source != null) {
             networkId = source.networkId;
             status = source.status;
-            disableReason = source.disableReason;
-            disableReason = source.disableReason;
             SSID = source.SSID;
             BSSID = source.BSSID;
             FQDN = source.FQDN;
@@ -1442,6 +1604,7 @@
             providerFriendlyName = source.providerFriendlyName;
             preSharedKey = source.preSharedKey;
 
+            mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus());
             apBand = source.apBand;
             apChannel = source.apChannel;
 
@@ -1458,7 +1621,6 @@
             allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
             allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
             allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
-
             enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
 
             defaultGwMacAddress = source.defaultGwMacAddress;
@@ -1476,7 +1638,6 @@
                 linkedConfigurations.putAll(source.linkedConfigurations);
             }
             mCachedConfigKey = null; //force null configKey
-            autoJoinStatus = source.autoJoinStatus;
             selfAdded = source.selfAdded;
             validatedInternetAccess = source.validatedInternetAccess;
             ephemeral = source.ephemeral;
@@ -1492,16 +1653,13 @@
             creatorName = source.creatorName;
             lastUpdateName = source.lastUpdateName;
             peerWifiConfiguration = source.peerWifiConfiguration;
-            blackListTimestamp = source.blackListTimestamp;
+
             lastConnected = source.lastConnected;
             lastDisconnected = source.lastDisconnected;
             lastConnectionFailure = source.lastConnectionFailure;
             lastRoamingFailure = source.lastRoamingFailure;
             lastRoamingFailureReason = source.lastRoamingFailureReason;
             roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
-            numConnectionFailures = source.numConnectionFailures;
-            numIpConfigFailures = source.numIpConfigFailures;
-            numAuthFailures = source.numAuthFailures;
             numScorerOverride = source.numScorerOverride;
             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
             numAssociation = source.numAssociation;
@@ -1512,16 +1670,12 @@
             numTicksAtBadRSSI = source.numTicksAtBadRSSI;
             numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
             numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
-            autoJoinBSSID = source.autoJoinBSSID;
-            autoJoinUseAggressiveJoinAttemptThreshold
-                    = source.autoJoinUseAggressiveJoinAttemptThreshold;
-            autoJoinBailedDueToLowRssi = source.autoJoinBailedDueToLowRssi;
-            dirty = source.dirty;
             userApproved = source.userApproved;
             numNoInternetAccessReports = source.numNoInternetAccessReports;
             noInternetAccessExpected = source.noInternetAccessExpected;
             creationTime = source.creationTime;
             updateTime = source.updateTime;
+            shared = source.shared;
         }
     }
 
@@ -1535,12 +1689,11 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(networkId);
         dest.writeInt(status);
-        dest.writeInt(disableReason);
+        mNetworkSelectionStatus.writeToParcel(dest);
         dest.writeString(SSID);
         dest.writeString(BSSID);
         dest.writeInt(apBand);
         dest.writeInt(apChannel);
-        dest.writeString(autoJoinBSSID);
         dest.writeString(FQDN);
         dest.writeString(providerFriendlyName);
         dest.writeInt(roamingConsortiumIds.length);
@@ -1568,7 +1721,6 @@
         dest.writeParcelable(mIpConfiguration, flags);
         dest.writeString(dhcpServer);
         dest.writeString(defaultGwMacAddress);
-        dest.writeInt(autoJoinStatus);
         dest.writeInt(selfAdded ? 1 : 0);
         dest.writeInt(didSelfAdd ? 1 : 0);
         dest.writeInt(validatedInternetAccess ? 1 : 0);
@@ -1578,14 +1730,10 @@
         dest.writeInt(lastUpdateUid);
         dest.writeString(creatorName);
         dest.writeString(lastUpdateName);
-        dest.writeLong(blackListTimestamp);
         dest.writeLong(lastConnectionFailure);
         dest.writeLong(lastRoamingFailure);
         dest.writeInt(lastRoamingFailureReason);
         dest.writeLong(roamingFailureBlackListTimeMilli);
-        dest.writeInt(numConnectionFailures);
-        dest.writeInt(numIpConfigFailures);
-        dest.writeInt(numAuthFailures);
         dest.writeInt(numScorerOverride);
         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
         dest.writeInt(numAssociation);
@@ -1596,11 +1744,10 @@
         dest.writeInt(numTicksAtBadRSSI);
         dest.writeInt(numTicksAtNotHighRSSI);
         dest.writeInt(numUserTriggeredJoinAttempts);
-        dest.writeInt(autoJoinUseAggressiveJoinAttemptThreshold);
-        dest.writeInt(autoJoinBailedDueToLowRssi ? 1 : 0);
         dest.writeInt(userApproved);
         dest.writeInt(numNoInternetAccessReports);
         dest.writeInt(noInternetAccessExpected ? 1 : 0);
+        dest.writeInt(shared ? 1 : 0);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -1610,12 +1757,11 @@
                 WifiConfiguration config = new WifiConfiguration();
                 config.networkId = in.readInt();
                 config.status = in.readInt();
-                config.disableReason = in.readInt();
+                config.mNetworkSelectionStatus.readFromParcel(in);
                 config.SSID = in.readString();
                 config.BSSID = in.readString();
                 config.apBand = in.readInt();
                 config.apChannel = in.readInt();
-                config.autoJoinBSSID = in.readString();
                 config.FQDN = in.readString();
                 config.providerFriendlyName = in.readString();
                 int numRoamingConsortiumIds = in.readInt();
@@ -1640,11 +1786,9 @@
                 config.allowedGroupCiphers    = readBitSet(in);
 
                 config.enterpriseConfig = in.readParcelable(null);
-
                 config.mIpConfiguration = in.readParcelable(null);
                 config.dhcpServer = in.readString();
                 config.defaultGwMacAddress = in.readString();
-                config.autoJoinStatus = in.readInt();
                 config.selfAdded = in.readInt() != 0;
                 config.didSelfAdd = in.readInt() != 0;
                 config.validatedInternetAccess = in.readInt() != 0;
@@ -1654,14 +1798,10 @@
                 config.lastUpdateUid = in.readInt();
                 config.creatorName = in.readString();
                 config.lastUpdateName = in.readString();
-                config.blackListTimestamp = in.readLong();
                 config.lastConnectionFailure = in.readLong();
                 config.lastRoamingFailure = in.readLong();
                 config.lastRoamingFailureReason = in.readInt();
                 config.roamingFailureBlackListTimeMilli = in.readLong();
-                config.numConnectionFailures = in.readInt();
-                config.numIpConfigFailures = in.readInt();
-                config.numAuthFailures = in.readInt();
                 config.numScorerOverride = in.readInt();
                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
                 config.numAssociation = in.readInt();
@@ -1672,11 +1812,10 @@
                 config.numTicksAtBadRSSI = in.readInt();
                 config.numTicksAtNotHighRSSI = in.readInt();
                 config.numUserTriggeredJoinAttempts = in.readInt();
-                config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
-                config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
                 config.userApproved = in.readInt();
                 config.numNoInternetAccessReports = in.readInt();
                 config.noInternetAccessExpected = in.readInt() != 0;
+                config.shared = in.readInt() != 0;
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7a5a74f..c1269f9 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1007,7 +1007,7 @@
 
     /**
      * @return true if this adapter supports Neighbour Awareness Network APIs
-     * @hide
+     * @hide PROPOSED_NAN_API
      */
     public boolean isNanSupported() {
         return isFeatureSupported(WIFI_FEATURE_NAN);
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/wifi/java/android/net/wifi/nan/ConfigRequest.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to wifi/java/android/net/wifi/nan/ConfigRequest.aidl
index 805fe2c..38dddc2 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/wifi/java/android/net/wifi/nan/ConfigRequest.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.net.wifi.nan;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable ConfigRequest;
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/nan/ConfigRequest.java
new file mode 100644
index 0000000..23e3754
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/ConfigRequest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Defines a request object to configure a Wi-Fi NAN network. Built using
+ * {@link ConfigRequest.Builder}. Configuration is requested using
+ * {@link WifiNanManager#requestConfig(ConfigRequest)}. Note that the actual
+ * achieved configuration may be different from the requested configuration -
+ * since multiple applications may request different configurations.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class ConfigRequest implements Parcelable {
+    /**
+     * Lower range of possible cluster ID.
+     *
+     * @hide
+     */
+    public static final int CLUSTER_ID_MIN = 0;
+
+    /**
+     * Upper range of possible cluster ID.
+     *
+     * @hide
+     */
+    public static final int CLUSTER_ID_MAX = 0xFFFF;
+
+    /**
+     * Indicates whether 5G band support is requested.
+     *
+     * @hide
+     */
+    public final boolean mSupport5gBand;
+
+    /**
+     * Specifies the desired master preference.
+     *
+     * @hide
+     */
+    public final int mMasterPreference;
+
+    /**
+     * Specifies the desired lower range of the cluster ID. Must be lower then
+     * {@link ConfigRequest#mClusterHigh}.
+     *
+     * @hide
+     */
+    public final int mClusterLow;
+
+    /**
+     * Specifies the desired higher range of the cluster ID. Must be higher then
+     * {@link ConfigRequest#mClusterLow}.
+     *
+     * @hide
+     */
+    public final int mClusterHigh;
+
+    private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow,
+            int clusterHigh) {
+        mSupport5gBand = support5gBand;
+        mMasterPreference = masterPreference;
+        mClusterLow = clusterLow;
+        mClusterHigh = clusterHigh;
+    }
+
+    @Override
+    public String toString() {
+        return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference="
+                + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh="
+                + mClusterHigh + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSupport5gBand ? 1 : 0);
+        dest.writeInt(mMasterPreference);
+        dest.writeInt(mClusterLow);
+        dest.writeInt(mClusterHigh);
+    }
+
+    public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() {
+        @Override
+        public ConfigRequest[] newArray(int size) {
+            return new ConfigRequest[size];
+        }
+
+        @Override
+        public ConfigRequest createFromParcel(Parcel in) {
+            boolean support5gBand = in.readInt() != 0;
+            int masterPreference = in.readInt();
+            int clusterLow = in.readInt();
+            int clusterHigh = in.readInt();
+            return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof ConfigRequest)) {
+            return false;
+        }
+
+        ConfigRequest lhs = (ConfigRequest) o;
+
+        return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
+                && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + (mSupport5gBand ? 1 : 0);
+        result = 31 * result + mMasterPreference;
+        result = 31 * result + mClusterLow;
+        result = 31 * result + mClusterHigh;
+
+        return result;
+    }
+
+    /**
+     * Builder used to build {@link ConfigRequest} objects.
+     */
+    public static final class Builder {
+        private boolean mSupport5gBand;
+        private int mMasterPreference;
+        private int mClusterLow;
+        private int mClusterHigh;
+
+        /**
+         * Default constructor for the Builder.
+         */
+        public Builder() {
+            mSupport5gBand = false;
+            mMasterPreference = 0;
+            mClusterLow = 0;
+            mClusterHigh = CLUSTER_ID_MAX;
+        }
+
+        /**
+         * Specify whether 5G band support is required in this request.
+         *
+         * @param support5gBand Support for 5G band is required.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setSupport5gBand(boolean support5gBand) {
+            mSupport5gBand = support5gBand;
+            return this;
+        }
+
+        /**
+         * Specify the Master Preference requested. The permitted range is 0 to
+         * 255 with 1 and 255 excluded (reserved).
+         *
+         * @param masterPreference The requested master preference
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setMasterPreference(int masterPreference) {
+            if (masterPreference < 0) {
+                throw new IllegalArgumentException(
+                        "Master Preference specification must be non-negative");
+            }
+            if (masterPreference == 1 || masterPreference == 255 || masterPreference > 255) {
+                throw new IllegalArgumentException("Master Preference specification must not "
+                        + "exceed 255 or use 1 or 255 (reserved values)");
+            }
+
+            mMasterPreference = masterPreference;
+            return this;
+        }
+
+        /**
+         * The Cluster ID is generated randomly for new NAN networks. Specify
+         * the lower range of the cluster ID. The upper range is specified using
+         * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
+         * range is 0 to the value specified by
+         * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality is
+         * permitted which restricts the Cluster ID to the specified value.
+         *
+         * @param clusterLow The lower range of the generated cluster ID.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
+         */
+        public Builder setClusterLow(int clusterLow) {
+            if (clusterLow < CLUSTER_ID_MIN) {
+                throw new IllegalArgumentException("Cluster specification must be non-negative");
+            }
+            if (clusterLow > CLUSTER_ID_MAX) {
+                throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+            }
+
+            mClusterLow = clusterLow;
+            return this;
+        }
+
+        /**
+         * The Cluster ID is generated randomly for new NAN networks. Specify
+         * the lower upper of the cluster ID. The lower range is specified using
+         * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
+         * range is the value specified by
+         * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF. Equality
+         * is permitted which restricts the Cluster ID to the specified value.
+         *
+         * @param clusterHigh The upper range of the generated cluster ID.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setClusterLow(..).setClusterHigh(..)}.
+         */
+        public Builder setClusterHigh(int clusterHigh) {
+            if (clusterHigh < CLUSTER_ID_MIN) {
+                throw new IllegalArgumentException("Cluster specification must be non-negative");
+            }
+            if (clusterHigh > CLUSTER_ID_MAX) {
+                throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+            }
+
+            mClusterHigh = clusterHigh;
+            return this;
+        }
+
+        /**
+         * Build {@link ConfigRequest} given the current requests made on the
+         * builder.
+         */
+        public ConfigRequest build() {
+            if (mClusterLow > mClusterHigh) {
+                throw new IllegalArgumentException(
+                        "Invalid argument combination - must have Cluster Low <= Cluster High");
+            }
+
+            return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
new file mode 100644
index 0000000..13efc36
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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.net.wifi.nan;
+
+import android.net.wifi.nan.ConfigRequest;
+
+/**
+ * Callback interface that WifiNanManager implements
+ *
+ * {@hide}
+ */
+oneway interface IWifiNanEventListener
+{
+    void onConfigCompleted(in ConfigRequest completedConfig);
+    void onConfigFailed(int reason);
+    void onNanDown(int reason);
+    void onIdentityChanged();
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
new file mode 100644
index 0000000..ff3d29f
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -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.
+ */
+
+package android.net.wifi.nan;
+
+import android.app.PendingIntent;
+
+import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.nan.IWifiNanEventListener;
+import android.net.wifi.nan.IWifiNanSessionListener;
+import android.net.wifi.nan.PublishData;
+import android.net.wifi.nan.PublishSettings;
+import android.net.wifi.nan.SubscribeData;
+import android.net.wifi.nan.SubscribeSettings;
+
+/**
+ * Interface that WifiNanService implements
+ *
+ * {@hide}
+ */
+interface IWifiNanManager
+{
+    // client API
+    void connect(in IBinder binder, in IWifiNanEventListener listener, int events);
+    void disconnect(in IBinder binder);
+    void requestConfig(in ConfigRequest configRequest);
+
+    // session API
+    int createSession(in IWifiNanSessionListener listener, int events);
+    void publish(int sessionId, in PublishData publishData, in PublishSettings publishSettings);
+    void subscribe(int sessionId, in SubscribeData subscribeData,
+            in SubscribeSettings subscribeSettings);
+    void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength);
+    void stopSession(int sessionId);
+    void destroySession(int sessionId);
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
new file mode 100644
index 0000000..773f83b
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
@@ -0,0 +1,38 @@
+/**
+ * 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.net.wifi.nan;
+
+/**
+ * Callback interface that WifiNanManager implements
+ *
+ * {@hide}
+ */
+oneway interface IWifiNanSessionListener
+{
+    void onPublishFail(int reason);
+    void onPublishTerminated(int reason);
+
+    void onSubscribeFail(int reason);
+    void onSubscribeTerminated(int reason);
+
+    void onMatch(int peerId, in byte[] serviceSpecificInfo,
+            int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength);
+
+    void onMessageSendSuccess();
+    void onMessageSendFail(int reason);
+    void onMessageReceived(int peerId, in byte[] message, int messageLength);
+}
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/wifi/java/android/net/wifi/nan/PublishData.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to wifi/java/android/net/wifi/nan/PublishData.aidl
index 805fe2c..15e4ddf 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/wifi/java/android/net/wifi/nan/PublishData.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.net.wifi.nan;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable PublishData;
diff --git a/wifi/java/android/net/wifi/nan/PublishData.java b/wifi/java/android/net/wifi/nan/PublishData.java
new file mode 100644
index 0000000..80119eb
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishData.java
@@ -0,0 +1,343 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Defines the data for a NAN publish session. Built using
+ * {@link PublishData.Builder}. Publish is done using
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}.
+ * @hide PROPOSED_NAN_API
+ */
+public class PublishData implements Parcelable {
+    /**
+     * @hide
+     */
+    public final String mServiceName;
+
+    /**
+     * @hide
+     */
+    public final int mServiceSpecificInfoLength;
+
+    /**
+     * @hide
+     */
+    public final byte[] mServiceSpecificInfo;
+
+    /**
+     * @hide
+     */
+    public final int mTxFilterLength;
+
+    /**
+     * @hide
+     */
+    public final byte[] mTxFilter;
+
+    /**
+     * @hide
+     */
+    public final int mRxFilterLength;
+
+    /**
+     * @hide
+     */
+    public final byte[] mRxFilter;
+
+    private PublishData(String serviceName, byte[] serviceSpecificInfo,
+            int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
+            int rxFilterLength) {
+        mServiceName = serviceName;
+        mServiceSpecificInfoLength = serviceSpecificInfoLength;
+        mServiceSpecificInfo = serviceSpecificInfo;
+        mTxFilterLength = txFilterLength;
+        mTxFilter = txFilter;
+        mRxFilterLength = rxFilterLength;
+        mRxFilter = rxFilter;
+    }
+
+    @Override
+    public String toString() {
+        return "PublishData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
+                + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
+                + "', mTxFilter="
+                + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
+                + ", mRxFilter="
+                + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
+                + "']";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mServiceName);
+        dest.writeInt(mServiceSpecificInfoLength);
+        if (mServiceSpecificInfoLength != 0) {
+            dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
+        }
+        dest.writeInt(mTxFilterLength);
+        if (mTxFilterLength != 0) {
+            dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
+        }
+        dest.writeInt(mRxFilterLength);
+        if (mRxFilterLength != 0) {
+            dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
+        }
+    }
+
+    public static final Creator<PublishData> CREATOR = new Creator<PublishData>() {
+        @Override
+        public PublishData[] newArray(int size) {
+            return new PublishData[size];
+        }
+
+        @Override
+        public PublishData createFromParcel(Parcel in) {
+            String serviceName = in.readString();
+            int ssiLength = in.readInt();
+            byte[] ssi = new byte[ssiLength];
+            if (ssiLength != 0) {
+                in.readByteArray(ssi);
+            }
+            int txFilterLength = in.readInt();
+            byte[] txFilter = new byte[txFilterLength];
+            if (txFilterLength != 0) {
+                in.readByteArray(txFilter);
+            }
+            int rxFilterLength = in.readInt();
+            byte[] rxFilter = new byte[rxFilterLength];
+            if (rxFilterLength != 0) {
+                in.readByteArray(rxFilter);
+            }
+
+            return new PublishData(serviceName, ssi, ssiLength, txFilter, txFilterLength, rxFilter,
+                    rxFilterLength);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PublishData)) {
+            return false;
+        }
+
+        PublishData lhs = (PublishData) o;
+
+        if (!mServiceName.equals(lhs.mServiceName)
+                || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
+                || mTxFilterLength != lhs.mTxFilterLength
+                || mRxFilterLength != lhs.mRxFilterLength) {
+            return false;
+        }
+
+        if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
+            for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
+                if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
+                    return false;
+                }
+            }
+        } else if (mServiceSpecificInfoLength != 0) {
+            return false; // invalid != invalid
+        }
+
+        if (mTxFilter != null && lhs.mTxFilter != null) {
+            for (int i = 0; i < mTxFilterLength; ++i) {
+                if (mTxFilter[i] != lhs.mTxFilter[i]) {
+                    return false;
+                }
+            }
+        } else if (mTxFilterLength != 0) {
+            return false; // invalid != invalid
+        }
+
+        if (mRxFilter != null && lhs.mRxFilter != null) {
+            for (int i = 0; i < mRxFilterLength; ++i) {
+                if (mRxFilter[i] != lhs.mRxFilter[i]) {
+                    return false;
+                }
+            }
+        } else if (mRxFilterLength != 0) {
+            return false; // invalid != invalid
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + mServiceName.hashCode();
+        result = 31 * result + mServiceSpecificInfoLength;
+        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
+        result = 31 * result + mTxFilterLength;
+        result = 31 * result + Arrays.hashCode(mTxFilter);
+        result = 31 * result + mRxFilterLength;
+        result = 31 * result + Arrays.hashCode(mRxFilter);
+
+        return result;
+    }
+
+    /**
+     * Builder used to build {@link PublishData} objects.
+     */
+    public static final class Builder {
+        private String mServiceName;
+        private int mServiceSpecificInfoLength;
+        private byte[] mServiceSpecificInfo = new byte[0];
+        private int mTxFilterLength;
+        private byte[] mTxFilter = new byte[0];
+        private int mRxFilterLength;
+        private byte[] mRxFilter = new byte[0];
+
+        /**
+         * Specify the service name of the publish session. The actual on-air
+         * value is a 6 byte hashed representation of this string.
+         *
+         * @param serviceName The service name for the publish session.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceName(String serviceName) {
+            mServiceName = serviceName;
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the publish session. This is
+         * a free-form byte array available to the application to send
+         * additional information as part of the discovery operation - i.e. it
+         * will not be used to determine whether a publish/subscribe match
+         * occurs.
+         *
+         * @param serviceSpecificInfo A byte-array for the service-specific
+         *            information field.
+         * @param serviceSpecificInfoLength The length of the byte-array to be
+         *            used.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
+                int serviceSpecificInfoLength) {
+            if (serviceSpecificInfoLength != 0 && (serviceSpecificInfo == null
+                    || serviceSpecificInfo.length < serviceSpecificInfoLength)) {
+                throw new IllegalArgumentException("Non-matching combination of "
+                        + "serviceSpecificInfo and serviceSpecificInfoLength");
+            }
+            mServiceSpecificInfoLength = serviceSpecificInfoLength;
+            mServiceSpecificInfo = serviceSpecificInfo;
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the publish session - same
+         * as {@link PublishData.Builder#setServiceSpecificInfo(byte[], int)}
+         * but obtaining the data from a String.
+         *
+         * @param serviceSpecificInfoStr The service specific information string
+         *            to be included (as a byte array) in the publish
+         *            information.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) {
+            mServiceSpecificInfoLength = serviceSpecificInfoStr.length();
+            mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
+            return this;
+        }
+
+        /**
+         * The transmit filter for an active publish session
+         * {@link PublishSettings.Builder#setPublishType(int)} and
+         * {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}. Included in
+         * transmitted publish packets and used by receivers (subscribers) to
+         * determine whether they match - in addition to just relying on the
+         * service name.
+         * <p>
+         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+         * is available to form and parse.
+         *
+         * @param txFilter The byte-array containing the LV formatted transmit
+         *            filter.
+         * @param txFilterLength The number of bytes in the transmit filter
+         *            argument.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
+            if (txFilterLength != 0 && (txFilter == null || txFilter.length < txFilterLength)) {
+                throw new IllegalArgumentException(
+                        "Non-matching combination of txFilter and txFilterLength");
+            }
+            mTxFilter = txFilter;
+            mTxFilterLength = txFilterLength;
+            return this;
+        }
+
+        /**
+         * The transmit filter for a passive publish session
+         * {@link PublishSettings.Builder#setPublishType(int)} and
+         * {@link PublishSettings#PUBLISH_TYPE_SOLICITED}. Used by the publisher
+         * to determine whether they match transmitted subscriber packets
+         * (active subscribers) - in addition to just relying on the service
+         * name.
+         * <p>
+         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+         * is available to form and parse.
+         *
+         * @param rxFilter The byte-array containing the LV formatted receive
+         *            filter.
+         * @param rxFilterLength The number of bytes in the receive filter
+         *            argument.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
+            if (rxFilterLength != 0 && (rxFilter == null || rxFilter.length < rxFilterLength)) {
+                throw new IllegalArgumentException(
+                        "Non-matching combination of rxFilter and rxFilterLength");
+            }
+            mRxFilter = rxFilter;
+            mRxFilterLength = rxFilterLength;
+            return this;
+        }
+
+        /**
+         * Build {@link PublishData} given the current requests made on the
+         * builder.
+         */
+        public PublishData build() {
+            return new PublishData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
+                    mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength);
+        }
+    }
+}
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/wifi/java/android/net/wifi/nan/PublishSettings.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to wifi/java/android/net/wifi/nan/PublishSettings.aidl
index 805fe2c..ff69293 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/wifi/java/android/net/wifi/nan/PublishSettings.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.net.wifi.nan;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable PublishSettings;
diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.java b/wifi/java/android/net/wifi/nan/PublishSettings.java
new file mode 100644
index 0000000..bbc5340
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishSettings.java
@@ -0,0 +1,204 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Defines the settings (configuration) for a NAN publish session. Built using
+ * {@link PublishSettings.Builder}. Publish is done using
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class PublishSettings implements Parcelable {
+
+    /**
+     * Defines an unsolicited publish session - i.e. a publish session where
+     * publish packets are transmitted over-the-air. Configuration is done using
+     * {@link PublishSettings.Builder#setPublishType(int)}.
+     */
+    public static final int PUBLISH_TYPE_UNSOLICITED = 0;
+
+    /**
+     * Defines a solicited publish session - i.e. a publish session where
+     * publish packets are not transmitted over-the-air and the device listens
+     * and matches to transmitted subscribe packets. Configuration is done using
+     * {@link PublishSettings.Builder#setPublishType(int)}.
+     */
+    public static final int PUBLISH_TYPE_SOLICITED = 1;
+
+    /**
+     * @hide
+     */
+    public final int mPublishType;
+
+    /**
+     * @hide
+     */
+    public final int mPublishCount;
+
+    /**
+     * @hide
+     */
+    public final int mTtlSec;
+
+    private PublishSettings(int publishType, int publichCount, int ttlSec) {
+        mPublishType = publishType;
+        mPublishCount = publichCount;
+        mTtlSec = ttlSec;
+    }
+
+    @Override
+    public String toString() {
+        return "PublishSettings [mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount
+                + ", mTtlSec=" + mTtlSec + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mPublishType);
+        dest.writeInt(mPublishCount);
+        dest.writeInt(mTtlSec);
+    }
+
+    public static final Creator<PublishSettings> CREATOR = new Creator<PublishSettings>() {
+        @Override
+        public PublishSettings[] newArray(int size) {
+            return new PublishSettings[size];
+        }
+
+        @Override
+        public PublishSettings createFromParcel(Parcel in) {
+            int publishType = in.readInt();
+            int publishCount = in.readInt();
+            int ttlSec = in.readInt();
+            return new PublishSettings(publishType, publishCount, ttlSec);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PublishSettings)) {
+            return false;
+        }
+
+        PublishSettings lhs = (PublishSettings) o;
+
+        return mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount
+                && mTtlSec == lhs.mTtlSec;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + mPublishType;
+        result = 31 * result + mPublishCount;
+        result = 31 * result + mTtlSec;
+
+        return result;
+    }
+
+    /**
+     * Builder used to build {@link PublishSettings} objects.
+     */
+    public static final class Builder {
+        int mPublishType;
+        int mPublishCount;
+        int mTtlSec;
+
+        /**
+         * Sets the type of the publish session: solicited (aka active - publish
+         * packets are transmitted over-the-air), or unsolicited (aka passive -
+         * no publish packets are transmitted, a match is made against an active
+         * subscribe session whose packets are transmitted over-the-air).
+         *
+         * @param publishType Publish session type: solicited (
+         *            {@link PublishSettings#PUBLISH_TYPE_SOLICITED}) or
+         *            unsolicited (
+         *            {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}).
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setPublishType(int publishType) {
+            if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) {
+                throw new IllegalArgumentException("Invalid publishType - " + publishType);
+            }
+            mPublishType = publishType;
+            return this;
+        }
+
+        /**
+         * Sets the number of times a solicited (
+         * {@link PublishSettings.Builder#setPublishType(int)}) publish session
+         * will transmit a packet. When the count is reached an event will be
+         * generated for {@link WifiNanSessionListener#onPublishTerminated(int)}
+         * with reason={@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+         *
+         * @param publishCount Number of publish packets to transmit.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setPublishCount(int publishCount) {
+            if (publishCount < 0) {
+                throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
+            }
+            mPublishCount = publishCount;
+            return this;
+        }
+
+        /**
+         * Sets the time interval (in seconds) a solicited (
+         * {@link PublishSettings.Builder#setPublishCount(int)}) publish session
+         * will be alive - i.e. transmitting a packet. When the TTL is reached
+         * an event will be generated for
+         * {@link WifiNanSessionListener#onPublishTerminated(int)} with reason=
+         * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+         *
+         * @param ttlSec Lifetime of a publish session in seconds.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setTtlSec(int ttlSec) {
+            if (ttlSec < 0) {
+                throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+            }
+            mTtlSec = ttlSec;
+            return this;
+        }
+
+        /**
+         * Build {@link PublishSettings} given the current requests made on the
+         * builder.
+         */
+        public PublishSettings build() {
+            return new PublishSettings(mPublishType, mPublishCount, mTtlSec);
+        }
+    }
+}
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/wifi/java/android/net/wifi/nan/SubscribeData.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to wifi/java/android/net/wifi/nan/SubscribeData.aidl
index 805fe2c..662fdb8 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/wifi/java/android/net/wifi/nan/SubscribeData.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.net.wifi.nan;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable SubscribeData;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.java b/wifi/java/android/net/wifi/nan/SubscribeData.java
new file mode 100644
index 0000000..cd6e918
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeData.java
@@ -0,0 +1,329 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Defines the data for a NAN subscribe session. Built using
+ * {@link SubscribeData.Builder}. Subscribe is done using
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * or
+ * {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}.
+ * @hide PROPOSED_NAN_API
+ */
+public class SubscribeData implements Parcelable {
+    /**
+     * @hide
+     */
+    public final String mServiceName;
+
+    /**
+     * @hide
+     */
+    public final int mServiceSpecificInfoLength;
+
+    /**
+     * @hide
+     */
+    public final byte[] mServiceSpecificInfo;
+
+    /**
+     * @hide
+     */
+    public final int mTxFilterLength;
+
+    /**
+     * @hide
+     */
+    public final byte[] mTxFilter;
+
+    /**
+     * @hide
+     */
+    public final int mRxFilterLength;
+
+    /**
+     * @hide
+     */
+    public final byte[] mRxFilter;
+
+    private SubscribeData(String serviceName, byte[] serviceSpecificInfo,
+            int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
+            int rxFilterLength) {
+        mServiceName = serviceName;
+        mServiceSpecificInfoLength = serviceSpecificInfoLength;
+        mServiceSpecificInfo = serviceSpecificInfo;
+        mTxFilterLength = txFilterLength;
+        mTxFilter = txFilter;
+        mRxFilterLength = rxFilterLength;
+        mRxFilter = rxFilter;
+    }
+
+    @Override
+    public String toString() {
+        return "SubscribeData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
+                + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
+                + "', mTxFilter="
+                + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
+                + ", mRxFilter="
+                + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
+                + "']";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mServiceName);
+        dest.writeInt(mServiceSpecificInfoLength);
+        if (mServiceSpecificInfoLength != 0) {
+            dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
+        }
+        dest.writeInt(mTxFilterLength);
+        if (mTxFilterLength != 0) {
+            dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
+        }
+        dest.writeInt(mRxFilterLength);
+        if (mRxFilterLength != 0) {
+            dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
+        }
+    }
+
+    public static final Creator<SubscribeData> CREATOR = new Creator<SubscribeData>() {
+        @Override
+        public SubscribeData[] newArray(int size) {
+            return new SubscribeData[size];
+        }
+
+        @Override
+        public SubscribeData createFromParcel(Parcel in) {
+            String serviceName = in.readString();
+            int ssiLength = in.readInt();
+            byte[] ssi = new byte[ssiLength];
+            if (ssiLength != 0) {
+                in.readByteArray(ssi);
+            }
+            int txFilterLength = in.readInt();
+            byte[] txFilter = new byte[txFilterLength];
+            if (txFilterLength != 0) {
+                in.readByteArray(txFilter);
+            }
+            int rxFilterLength = in.readInt();
+            byte[] rxFilter = new byte[rxFilterLength];
+            if (rxFilterLength != 0) {
+                in.readByteArray(rxFilter);
+            }
+
+            return new SubscribeData(serviceName, ssi, ssiLength, txFilter, txFilterLength,
+                    rxFilter, rxFilterLength);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof SubscribeData)) {
+            return false;
+        }
+
+        SubscribeData lhs = (SubscribeData) o;
+
+        if (!mServiceName.equals(lhs.mServiceName)
+                || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
+                || mTxFilterLength != lhs.mTxFilterLength
+                || mRxFilterLength != lhs.mRxFilterLength) {
+            return false;
+        }
+
+        if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
+            for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
+                if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
+                    return false;
+                }
+            }
+        } else if (mServiceSpecificInfoLength != 0) {
+            return false; // invalid != invalid
+        }
+
+        if (mTxFilter != null && lhs.mTxFilter != null) {
+            for (int i = 0; i < mTxFilterLength; ++i) {
+                if (mTxFilter[i] != lhs.mTxFilter[i]) {
+                    return false;
+                }
+            }
+        } else if (mTxFilterLength != 0) {
+            return false; // invalid != invalid
+        }
+
+        if (mRxFilter != null && lhs.mRxFilter != null) {
+            for (int i = 0; i < mRxFilterLength; ++i) {
+                if (mRxFilter[i] != lhs.mRxFilter[i]) {
+                    return false;
+                }
+            }
+        } else if (mRxFilterLength != 0) {
+            return false; // invalid != invalid
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + mServiceName.hashCode();
+        result = 31 * result + mServiceSpecificInfoLength;
+        result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
+        result = 31 * result + mTxFilterLength;
+        result = 31 * result + Arrays.hashCode(mTxFilter);
+        result = 31 * result + mRxFilterLength;
+        result = 31 * result + Arrays.hashCode(mRxFilter);
+
+        return result;
+    }
+
+    /**
+     * Builder used to build {@link SubscribeData} objects.
+     */
+    public static final class Builder {
+        private String mServiceName;
+        private int mServiceSpecificInfoLength;
+        private byte[] mServiceSpecificInfo = new byte[0];
+        private int mTxFilterLength;
+        private byte[] mTxFilter = new byte[0];
+        private int mRxFilterLength;
+        private byte[] mRxFilter = new byte[0];
+
+        /**
+         * Specify the service name of the subscribe session. The actual on-air
+         * value is a 6 byte hashed representation of this string.
+         *
+         * @param serviceName The service name for the subscribe session.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceName(String serviceName) {
+            mServiceName = serviceName;
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the subscribe session. This
+         * is a free-form byte array available to the application to send
+         * additional information as part of the discovery operation - i.e. it
+         * will not be used to determine whether a publish/subscribe match
+         * occurs.
+         *
+         * @param serviceSpecificInfo A byte-array for the service-specific
+         *            information field.
+         * @param serviceSpecificInfoLength The length of the byte-array to be
+         *            used.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
+                int serviceSpecificInfoLength) {
+            mServiceSpecificInfoLength = serviceSpecificInfoLength;
+            mServiceSpecificInfo = serviceSpecificInfo;
+            return this;
+        }
+
+        /**
+         * Specify service specific information for the subscribe session - same
+         * as {@link SubscribeData.Builder#setServiceSpecificInfo(byte[], int)}
+         * but obtaining the data from a String.
+         *
+         * @param serviceSpecificInfoStr The service specific information string
+         *            to be included (as a byte array) in the subscribe
+         *            information.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) {
+            mServiceSpecificInfoLength = serviceSpecificInfoStr.length();
+            mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
+            return this;
+        }
+
+        /**
+         * The transmit filter for an active subscribe session
+         * {@link SubscribeSettings.Builder#setSubscribeType(int)} and
+         * {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}. Included in
+         * transmitted subscribe packets and used by receivers (passive
+         * publishers) to determine whether they match - in addition to just
+         * relying on the service name.
+         * <p>
+         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+         * is available to form and parse.
+         *
+         * @param txFilter The byte-array containing the LV formatted transmit
+         *            filter.
+         * @param txFilterLength The number of bytes in the transmit filter
+         *            argument.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
+            mTxFilter = txFilter;
+            mTxFilterLength = txFilterLength;
+            return this;
+        }
+
+        /**
+         * The transmit filter for a passive subsribe session
+         * {@link SubscribeSettings.Builder#setSubscribeType(int)} and
+         * {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}. Used by the
+         * subscriber to determine whether they match transmitted publish
+         * packets - in addition to just relying on the service name.
+         * <p>
+         * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+         * is available to form and parse.
+         *
+         * @param rxFilter The byte-array containing the LV formatted receive
+         *            filter.
+         * @param rxFilterLength The number of bytes in the receive filter
+         *            argument.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
+            mRxFilter = rxFilter;
+            mRxFilterLength = rxFilterLength;
+            return this;
+        }
+
+        /**
+         * Build {@link SubscribeData} given the current requests made on the
+         * builder.
+         */
+        public SubscribeData build() {
+            return new SubscribeData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
+                    mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength);
+        }
+    }
+}
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
similarity index 73%
copy from core/java/android/service/notification/NotificationAdjustment.aidl
copy to wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
index 805fe2c..44849bc 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2015, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.notification;
+package android.net.wifi.nan;
 
-parcelable NotificationAdjustment;
\ No newline at end of file
+parcelable SubscribeSettings;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.java b/wifi/java/android/net/wifi/nan/SubscribeSettings.java
new file mode 100644
index 0000000..5c4f8fb
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeSettings.java
@@ -0,0 +1,205 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Defines the settings (configuration) for a NAN subscribe session. Built using
+ * {@link SubscribeSettings.Builder}. Subscribe is done using
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * or {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class SubscribeSettings implements Parcelable {
+
+    /**
+     * Defines a passive subscribe session - i.e. a subscribe session where
+     * subscribe packets are not transmitted over-the-air and the device listens
+     * and matches to transmitted publish packets. Configuration is done using
+     * {@link SubscribeSettings.Builder#setSubscribeType(int)}.
+     */
+    public static final int SUBSCRIBE_TYPE_PASSIVE = 0;
+
+    /**
+     * Defines an active subscribe session - i.e. a subscribe session where
+     * subscribe packets are transmitted over-the-air. Configuration is done
+     * using {@link SubscribeSettings.Builder#setSubscribeType(int)}.
+     */
+    public static final int SUBSCRIBE_TYPE_ACTIVE = 1;
+
+    /**
+     * @hide
+     */
+    public final int mSubscribeType;
+
+    /**
+     * @hide
+     */
+    public final int mSubscribeCount;
+
+    /**
+     * @hide
+     */
+    public final int mTtlSec;
+
+    private SubscribeSettings(int subscribeType, int publichCount, int ttlSec) {
+        mSubscribeType = subscribeType;
+        mSubscribeCount = publichCount;
+        mTtlSec = ttlSec;
+    }
+
+    @Override
+    public String toString() {
+        return "SubscribeSettings [mSubscribeType=" + mSubscribeType + ", mSubscribeCount="
+                + mSubscribeCount + ", mTtlSec=" + mTtlSec + "]";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSubscribeType);
+        dest.writeInt(mSubscribeCount);
+        dest.writeInt(mTtlSec);
+    }
+
+    public static final Creator<SubscribeSettings> CREATOR = new Creator<SubscribeSettings>() {
+        @Override
+        public SubscribeSettings[] newArray(int size) {
+            return new SubscribeSettings[size];
+        }
+
+        @Override
+        public SubscribeSettings createFromParcel(Parcel in) {
+            int subscribeType = in.readInt();
+            int subscribeCount = in.readInt();
+            int ttlSec = in.readInt();
+            return new SubscribeSettings(subscribeType, subscribeCount, ttlSec);
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof SubscribeSettings)) {
+            return false;
+        }
+
+        SubscribeSettings lhs = (SubscribeSettings) o;
+
+        return mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount
+                && mTtlSec == lhs.mTtlSec;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + mSubscribeType;
+        result = 31 * result + mSubscribeCount;
+        result = 31 * result + mTtlSec;
+
+        return result;
+    }
+
+    /**
+     * Builder used to build {@link SubscribeSettings} objects.
+     */
+    public static final class Builder {
+        int mSubscribeType;
+        int mSubscribeCount;
+        int mTtlSec;
+
+        /**
+         * Sets the type of the subscribe session: active (subscribe packets are
+         * transmitted over-the-air), or passive (no subscribe packets are
+         * transmitted, a match is made against a solicited/active publish
+         * session whose packets are transmitted over-the-air).
+         *
+         * @param subscribeType Subscribe session type: active (
+         *            {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}) or
+         *            passive ( {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}
+         *            ).
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setSubscribeType(int subscribeType) {
+            if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) {
+                throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType);
+            }
+            mSubscribeType = subscribeType;
+            return this;
+        }
+
+        /**
+         * Sets the number of times an active (
+         * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe
+         * session will transmit a packet. When the count is reached an event
+         * will be generated for
+         * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason=
+         * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+         *
+         * @param subscribeCount Number of subscribe packets to transmit.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setSubscribeCount(int subscribeCount) {
+            if (subscribeCount < 0) {
+                throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
+            }
+            mSubscribeCount = subscribeCount;
+            return this;
+        }
+
+        /**
+         * Sets the time interval (in seconds) an active (
+         * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe
+         * session will be alive - i.e. transmitting a packet. When the TTL is
+         * reached an event will be generated for
+         * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason=
+         * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+         *
+         * @param ttlSec Lifetime of a subscribe session in seconds.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setTtlSec(int ttlSec) {
+            if (ttlSec < 0) {
+                throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+            }
+            mTtlSec = ttlSec;
+            return this;
+        }
+
+        /**
+         * Build {@link SubscribeSettings} given the current requests made on
+         * the builder.
+         */
+        public SubscribeSettings build() {
+            return new SubscribeSettings(mSubscribeType, mSubscribeCount, mTtlSec);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
new file mode 100644
index 0000000..ea8785a
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
@@ -0,0 +1,490 @@
+/*
+ * 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.net.wifi.nan;
+
+import libcore.io.Memory;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteOrder;
+import java.util.Iterator;
+
+/**
+ * Utility class to construct and parse byte arrays using the TLV format -
+ * Type/Length/Value format. The utilities accept a configuration of the size of
+ * the Type field and the Length field. A Type field size of 0 is allowed -
+ * allowing usage for LV (no T) array formats.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class TlvBufferUtils {
+    private TlvBufferUtils() {
+        // no reason to ever create this class
+    }
+
+    /**
+     * Utility class to construct byte arrays using the TLV format -
+     * Type/Length/Value.
+     * <p>
+     * A constructor is created specifying the size of the Type (T) and Length
+     * (L) fields. A specification of zero size T field is allowed - resulting
+     * in LV type format.
+     * <p>
+     * The byte array is either provided (using
+     * {@link TlvConstructor#wrap(byte[])}) or allocated (using
+     * {@link TlvConstructor#allocate(int)}).
+     * <p>
+     * Values are added to the structure using the {@code TlvConstructor.put*()}
+     * methods.
+     * <p>
+     * The final byte array is obtained using {@link TlvConstructor#getArray()}
+     * and {@link TlvConstructor#getActualLength()} methods.
+     */
+    public static class TlvConstructor {
+        private int mTypeSize;
+        private int mLengthSize;
+
+        private byte[] mArray;
+        private int mArrayLength;
+        private int mPosition;
+
+        /**
+         * Define a TLV constructor with the specified size of the Type (T) and
+         * Length (L) fields.
+         *
+         * @param typeSize Number of bytes used for the Type (T) field. Values
+         *            of 0, 1, or 2 bytes are allowed. A specification of 0
+         *            bytes implies that the field being constructed has the LV
+         *            format rather than the TLV format.
+         * @param lengthSize Number of bytes used for the Length (L) field.
+         *            Values of 1 or 2 bytes are allowed.
+         */
+        public TlvConstructor(int typeSize, int lengthSize) {
+            if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
+                throw new IllegalArgumentException(
+                        "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
+            }
+            mTypeSize = typeSize;
+            mLengthSize = lengthSize;
+        }
+
+        /**
+         * Set the byte array to be used to construct the TLV.
+         *
+         * @param array Byte array to be formatted.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor wrap(byte[] array) {
+            mArray = array;
+            mArrayLength = array.length;
+            return this;
+        }
+
+        /**
+         * Allocates a new byte array to be used ot construct a TLV.
+         *
+         * @param capacity The size of the byte array to be allocated.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor allocate(int capacity) {
+            mArray = new byte[capacity];
+            mArrayLength = capacity;
+            return this;
+        }
+
+        /**
+         * Copies a byte into the TLV with the indicated type. For an LV
+         * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+         * TlvConstructor(int, int)} ) the type field is ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @param b The byte to be inserted into the structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putByte(int type, byte b) {
+            checkLength(1);
+            addHeader(type, 1);
+            mArray[mPosition++] = b;
+            return this;
+        }
+
+        /**
+         * Copies a byte array into the TLV with the indicated type. For an LV
+         * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+         * TlvConstructor(int, int)} ) the type field is ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @param array The array to be copied into the TLV structure.
+         * @param offset Start copying from the array at the specified offset.
+         * @param length Copy the specified number (length) of bytes from the
+         *            array.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putByteArray(int type, byte[] array, int offset, int length) {
+            checkLength(length);
+            addHeader(type, length);
+            System.arraycopy(array, offset, mArray, mPosition, length);
+            mPosition += length;
+            return this;
+        }
+
+        /**
+         * Copies a byte array into the TLV with the indicated type. For an LV
+         * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+         * TlvConstructor(int, int)} ) the type field is ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @param array The array to be copied (in full) into the TLV structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putByteArray(int type, byte[] array) {
+            return putByteArray(type, array, 0, array.length);
+        }
+
+        /**
+         * Places a zero length element (i.e. Length field = 0) into the TLV.
+         * For an LV formatted structure (i.e. typeLength=0 in
+         * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
+         * ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putZeroLengthElement(int type) {
+            checkLength(0);
+            addHeader(type, 0);
+            return this;
+        }
+
+        /**
+         * Copies short into the TLV with the indicated type. For an LV
+         * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+         * TlvConstructor(int, int)} ) the type field is ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @param data The short to be inserted into the structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putShort(int type, short data) {
+            checkLength(2);
+            addHeader(type, 2);
+            Memory.pokeShort(mArray, mPosition, data, ByteOrder.BIG_ENDIAN);
+            mPosition += 2;
+            return this;
+        }
+
+        /**
+         * Copies integer into the TLV with the indicated type. For an LV
+         * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+         * TlvConstructor(int, int)} ) the type field is ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @param data The integer to be inserted into the structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putInt(int type, int data) {
+            checkLength(4);
+            addHeader(type, 4);
+            Memory.pokeInt(mArray, mPosition, data, ByteOrder.BIG_ENDIAN);
+            mPosition += 4;
+            return this;
+        }
+
+        /**
+         * Copies a String's byte representation into the TLV with the indicated
+         * type. For an LV formatted structure (i.e. typeLength=0 in
+         * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
+         * ignored.
+         *
+         * @param type The value to be placed into the Type field.
+         * @param data The string whose bytes are to be inserted into the
+         *            structure.
+         * @return The constructor to facilitate chaining
+         *         {@code ctr.putXXX(..).putXXX(..)}.
+         */
+        public TlvConstructor putString(int type, String data) {
+            return putByteArray(type, data.getBytes(), 0, data.length());
+        }
+
+        /**
+         * Returns the constructed TLV formatted byte-array. Note that the
+         * returned array is the fully wrapped (
+         * {@link TlvConstructor#wrap(byte[])}) or allocated (
+         * {@link TlvConstructor#allocate(int)}) array - which isn't necessarily
+         * the actual size of the formatted data. Use
+         * {@link TlvConstructor#getActualLength()} to obtain the size of the
+         * formatted data.
+         *
+         * @return The byte array containing the TLV formatted structure.
+         */
+        public byte[] getArray() {
+            return mArray;
+        }
+
+        /**
+         * Returns the size of the TLV formatted portion of the wrapped or
+         * allocated byte array. The array itself is returned with
+         * {@link TlvConstructor#getArray()}.
+         *
+         * @return The size of the TLV formatted portion of the byte array.
+         */
+        public int getActualLength() {
+            return mPosition;
+        }
+
+        private void checkLength(int dataLength) {
+            if (mPosition + mTypeSize + mLengthSize + dataLength > mArrayLength) {
+                throw new BufferOverflowException();
+            }
+        }
+
+        private void addHeader(int type, int length) {
+            if (mTypeSize == 1) {
+                mArray[mPosition] = (byte) type;
+            } else if (mTypeSize == 2) {
+                Memory.pokeShort(mArray, mPosition, (short) type, ByteOrder.BIG_ENDIAN);
+            }
+            mPosition += mTypeSize;
+
+            if (mLengthSize == 1) {
+                mArray[mPosition] = (byte) length;
+            } else if (mLengthSize == 2) {
+                Memory.pokeShort(mArray, mPosition, (short) length, ByteOrder.BIG_ENDIAN);
+            }
+            mPosition += mLengthSize;
+        }
+    }
+
+    /**
+     * Utility class used when iterating over a TLV formatted byte-array. Use
+     * {@link TlvIterable} to iterate over array. A {@link TlvElement}
+     * represents each entry in a TLV formatted byte-array.
+     */
+    public static class TlvElement {
+        /**
+         * The Type (T) field of the current TLV element. Note that for LV
+         * formatted byte-arrays (i.e. TLV whose Type/T size is 0) the value of
+         * this field is undefined.
+         */
+        public int mType;
+
+        /**
+         * The Length (L) field of the current TLV element.
+         */
+        public int mLength;
+
+        /**
+         * The Value (V) field - a raw byte array representing the current TLV
+         * element where the entry starts at {@link TlvElement#mOffset}.
+         */
+        public byte[] mRefArray;
+
+        /**
+         * The offset to be used into {@link TlvElement#mRefArray} to access the
+         * raw data representing the current TLV element.
+         */
+        public int mOffset;
+
+        private TlvElement(int type, int length, byte[] refArray, int offset) {
+            mType = type;
+            mLength = length;
+            mRefArray = refArray;
+            mOffset = offset;
+        }
+
+        /**
+         * Utility function to return a byte representation of a TLV element of
+         * length 1. Note: an attempt to call this function on a TLV item whose
+         * {@link TlvElement#mLength} is != 1 will result in an exception.
+         *
+         * @return byte representation of current TLV element.
+         */
+        public byte getByte() {
+            if (mLength != 1) {
+                throw new IllegalArgumentException(
+                        "Accesing a byte from a TLV element of length " + mLength);
+            }
+            return mRefArray[mOffset];
+        }
+
+        /**
+         * Utility function to return a short representation of a TLV element of
+         * length 2. Note: an attempt to call this function on a TLV item whose
+         * {@link TlvElement#mLength} is != 2 will result in an exception.
+         *
+         * @return short representation of current TLV element.
+         */
+        public short getShort() {
+            if (mLength != 2) {
+                throw new IllegalArgumentException(
+                        "Accesing a short from a TLV element of length " + mLength);
+            }
+            return Memory.peekShort(mRefArray, mOffset, ByteOrder.BIG_ENDIAN);
+        }
+
+        /**
+         * Utility function to return an integer representation of a TLV element
+         * of length 4. Note: an attempt to call this function on a TLV item
+         * whose {@link TlvElement#mLength} is != 4 will result in an exception.
+         *
+         * @return integer representation of current TLV element.
+         */
+        public int getInt() {
+            if (mLength != 4) {
+                throw new IllegalArgumentException(
+                        "Accesing an int from a TLV element of length " + mLength);
+            }
+            return Memory.peekInt(mRefArray, mOffset, ByteOrder.BIG_ENDIAN);
+        }
+
+        /**
+         * Utility function to return a String representation of a TLV element.
+         *
+         * @return String repersentation of the current TLV element.
+         */
+        public String getString() {
+            return new String(mRefArray, mOffset, mLength);
+        }
+    }
+
+    /**
+     * Utility class to iterate over a TLV formatted byte-array.
+     */
+    public static class TlvIterable implements Iterable<TlvElement> {
+        private int mTypeSize;
+        private int mLengthSize;
+        private byte[] mArray;
+        private int mArrayLength;
+
+        /**
+         * Constructs a TlvIterable object - specifying the format of the TLV
+         * (the sizes of the Type and Length fields), and the byte array whose
+         * data is to be parsed.
+         *
+         * @param typeSize Number of bytes used for the Type (T) field. Valid
+         *            values are 0 (i.e. indicating the format is LV rather than
+         *            TLV), 1, and 2 bytes.
+         * @param lengthSize Number of bytes sued for the Length (L) field.
+         *            Values values are 1 or 2 bytes.
+         * @param array The TLV formatted byte-array to parse.
+         * @param length The number of bytes of the array to be used in the
+         *            parsing.
+         */
+        public TlvIterable(int typeSize, int lengthSize, byte[] array, int length) {
+            if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
+                throw new IllegalArgumentException(
+                        "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
+            }
+            mTypeSize = typeSize;
+            mLengthSize = lengthSize;
+            mArray = array;
+            mArrayLength = length;
+        }
+
+        /**
+         * Prints out a parsed representation of the TLV-formatted byte array.
+         * Whenever possible bytes, shorts, and integer are printed out (for
+         * fields whose length is 1, 2, or 4 respectively).
+         */
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+
+            builder.append("[");
+            boolean first = true;
+            for (TlvElement tlv : this) {
+                if (!first) {
+                    builder.append(",");
+                }
+                first = false;
+                builder.append(" (");
+                if (mTypeSize != 0) {
+                    builder.append("T=" + tlv.mType + ",");
+                }
+                builder.append("L=" + tlv.mLength + ") ");
+                if (tlv.mLength == 0) {
+                    builder.append("<null>");
+                } else if (tlv.mLength == 1) {
+                    builder.append(tlv.getByte());
+                } else if (tlv.mLength == 2) {
+                    builder.append(tlv.getShort());
+                } else if (tlv.mLength == 4) {
+                    builder.append(tlv.getInt());
+                } else {
+                    builder.append("<bytes>");
+                }
+                if (tlv.mLength != 0) {
+                    builder.append(" (S='" + tlv.getString() + "')");
+                }
+            }
+            builder.append("]");
+
+            return builder.toString();
+        }
+
+        /**
+         * Returns an iterator to step through a TLV formatted byte-array. The
+         * individual elements returned by the iterator are {@link TlvElement}.
+         */
+        @Override
+        public Iterator<TlvElement> iterator() {
+            return new Iterator<TlvElement>() {
+                private int mOffset = 0;
+
+                @Override
+                public boolean hasNext() {
+                    return mOffset < mArrayLength;
+                }
+
+                @Override
+                public TlvElement next() {
+                    int type = 0;
+                    if (mTypeSize == 1) {
+                        type = mArray[mOffset];
+                    } else if (mTypeSize == 2) {
+                        type = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN);
+                    }
+                    mOffset += mTypeSize;
+
+                    int length = 0;
+                    if (mLengthSize == 1) {
+                        length = mArray[mOffset];
+                    } else if (mLengthSize == 2) {
+                        length = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN);
+                    }
+                    mOffset += mLengthSize;
+
+                    TlvElement tlv = new TlvElement(type, length, mArray, mOffset);
+                    mOffset += length;
+                    return tlv;
+                }
+
+                @Override
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
new file mode 100644
index 0000000..eae0a55
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -0,0 +1,201 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * Base class for NAN events callbacks. Should be extended by applications
+ * wanting notifications. These are callbacks applying to the NAN connection as
+ * a whole - not to specific publish or subscribe sessions - for that see
+ * {@link WifiNanSessionListener}.
+ * <p>
+ * During registration specify which specific events are desired using a set of
+ * {@code NanEventListener.LISTEN_*} flags OR'd together. Only those events will
+ * be delivered to the registered listener. Override those callbacks
+ * {@code NanEventListener.on*} for the registered events.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanEventListener {
+    private static final String TAG = "WifiNanEventListener";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false; // STOPSHIP if true
+
+    /**
+     * Configuration completion callback event registration flag. Corresponding
+     * callback is {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)}.
+     */
+    public static final int LISTEN_CONFIG_COMPLETED = 0x1 << 0;
+
+    /**
+     * Configuration failed callback event registration flag. Corresponding
+     * callback is {@link WifiNanEventListener#onConfigFailed(int)}.
+     */
+    public static final int LISTEN_CONFIG_FAILED = 0x1 << 1;
+
+    /**
+     * NAN cluster is down callback event registration flag. Corresponding
+     * callback is {@link WifiNanEventListener#onNanDown(int)}.
+     */
+    public static final int LISTEN_NAN_DOWN = 0x1 << 2;
+
+    /**
+     * NAN identity has changed event registration flag. This may be due to
+     * joining a cluster, starting a cluster, or discovery interface change. The
+     * implication is that peers you've been communicating with may no longer
+     * recognize you and you need to re-establish your identity. Corresponding
+     * callback is {@link WifiNanEventListener#onIdentityChanged()}.
+     */
+    public static final int LISTEN_IDENTITY_CHANGED = 0x1 << 3;
+
+    private final Handler mHandler;
+
+    /**
+     * Constructs a {@link WifiNanEventListener} using the looper of the current
+     * thread. I.e. all callbacks will be delivered on the current thread.
+     */
+    public WifiNanEventListener() {
+        this(Looper.myLooper());
+    }
+
+    /**
+     * Constructs a {@link WifiNanEventListener} using the specified looper. I.e.
+     * all callbacks will delivered on the thread of the specified looper.
+     *
+     * @param looper The looper on which to execute the callbacks.
+     */
+    public WifiNanEventListener(Looper looper) {
+        if (VDBG) Log.v(TAG, "ctor: looper=" + looper);
+        mHandler = new Handler(looper) {
+            @Override
+            public void handleMessage(Message msg) {
+                if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+                switch (msg.what) {
+                    case LISTEN_CONFIG_COMPLETED:
+                        WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj);
+                        break;
+                    case LISTEN_CONFIG_FAILED:
+                        WifiNanEventListener.this.onConfigFailed(msg.arg1);
+                        break;
+                    case LISTEN_NAN_DOWN:
+                        WifiNanEventListener.this.onNanDown(msg.arg1);
+                        break;
+                    case LISTEN_IDENTITY_CHANGED:
+                        WifiNanEventListener.this.onIdentityChanged();
+                        break;
+                }
+            }
+        };
+    }
+
+    /**
+     * Called when NAN configuration is completed. Event will only be delivered
+     * if registered using {@link WifiNanEventListener#LISTEN_CONFIG_COMPLETED}. A
+     * dummy (empty implementation printing out a warning). Make sure to
+     * override if registered.
+     *
+     * @param completedConfig The actual configuration request which was
+     *            completed. Note that it may be different from that requested
+     *            by the application. The service combines configuration
+     *            requests from all applications.
+     */
+    public void onConfigCompleted(ConfigRequest completedConfig) {
+        Log.w(TAG, "onConfigCompleted: called in stub - override if interested or disable");
+    }
+
+    /**
+     * Called when NAN configuration failed. Event will only be delivered if
+     * registered using {@link WifiNanEventListener#LISTEN_CONFIG_FAILED}. A dummy
+     * (empty implementation printing out a warning). Make sure to override if
+     * registered.
+     *
+     * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}.
+     */
+    public void onConfigFailed(int reason) {
+        Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable");
+    }
+
+    /**
+     * Called when NAN cluster is down. Event will only be delivered if
+     * registered using {@link WifiNanEventListener#LISTEN_NAN_DOWN}. A dummy (empty
+     * implementation printing out a warning). Make sure to override if
+     * registered.
+     *
+     * @param reason Reason code for event, see {@code NanSessionListener.FAIL_*}.
+     */
+    public void onNanDown(int reason) {
+        Log.w(TAG, "onNanDown: called in stub - override if interested or disable");
+    }
+
+    /**
+     * Called when NAN identity has changed. This may be due to joining a
+     * cluster, starting a cluster, or discovery interface change. The
+     * implication is that peers you've been communicating with may no longer
+     * recognize you and you need to re-establish your identity. Event will only
+     * be delivered if registered using
+     * {@link WifiNanEventListener#LISTEN_IDENTITY_CHANGED}. A dummy (empty
+     * implementation printing out a warning). Make sure to override if
+     * registered.
+     */
+    public void onIdentityChanged() {
+        if (VDBG) Log.v(TAG, "onIdentityChanged: called in stub - override if interested");
+    }
+
+    /**
+     * {@hide}
+     */
+    public IWifiNanEventListener callback = new IWifiNanEventListener.Stub() {
+        @Override
+        public void onConfigCompleted(ConfigRequest completedConfig) {
+            if (VDBG) Log.v(TAG, "onConfigCompleted: configRequest=" + completedConfig);
+
+            Message msg = mHandler.obtainMessage(LISTEN_CONFIG_COMPLETED);
+            msg.obj = completedConfig;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onConfigFailed(int reason) {
+            if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onNanDown(int reason) {
+            if (VDBG) Log.v(TAG, "onNanDown: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_NAN_DOWN);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onIdentityChanged() {
+            if (VDBG) Log.v(TAG, "onIdentityChanged");
+
+            Message msg = mHandler.obtainMessage(LISTEN_IDENTITY_CHANGED);
+            mHandler.sendMessage(msg);
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
new file mode 100644
index 0000000..877f993
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -0,0 +1,333 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides the primary API for managing Wi-Fi NAN operation:
+ * including discovery and data-links. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String)
+ * Context.getSystemService(Context.WIFI_NAN_SERVICE)}.
+ * <p>
+ * The class provides access to:
+ * <ul>
+ * <li>Configure a NAN connection and register for events.
+ * <li>Create publish and subscribe sessions.
+ * <li>Create NAN network specifier to be used to create a NAN network.
+ * </ul>
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanManager {
+    private static final String TAG = "WifiNanManager";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false; // STOPSHIP if true
+
+    private IBinder mBinder;
+
+    private IWifiNanManager mService;
+
+    /**
+     * {@hide}
+     */
+    public WifiNanManager(IWifiNanManager service) {
+        mService = service;
+    }
+
+    /**
+     * Re-connect to the Wi-Fi NAN service - enabling the application to execute
+     * {@link WifiNanManager} APIs. Application don't normally need to call this
+     * API since it is executed in the constructor. However, applications which
+     * have explicitly {@link WifiNanManager#disconnect()} need to call this
+     * function to re-connect.
+     *
+     * @param listener A listener extended from {@link WifiNanEventListener}.
+     * @param events The set of events to be delivered to the {@code listener}.
+     *            OR'd event flags from {@link WifiNanEventListener
+     *            NanEventListener.LISTEN*}.
+     */
+    public void connect(WifiNanEventListener listener, int events) {
+        try {
+            if (VDBG) Log.v(TAG, "connect()");
+            if (listener == null) {
+                throw new IllegalArgumentException("Invalid listener - must not be null");
+            }
+            if (mBinder == null) {
+                mBinder = new Binder();
+            }
+            mService.connect(mBinder, listener.callback, events);
+        } catch (RemoteException e) {
+            Log.w(TAG, "connect RemoteException (FYI - ignoring): " + e);
+        }
+    }
+
+    /**
+     * Disconnect from the Wi-Fi NAN service and destroy all outstanding
+     * operations - i.e. all publish and subscribes are terminated, any
+     * outstanding data-link is shut-down, and all requested NAN configurations
+     * are cancelled.
+     * <p>
+     * An application may then re-connect using
+     * {@link WifiNanManager#connect(WifiNanEventListener, int)} .
+     */
+    public void disconnect() {
+        try {
+            if (VDBG) Log.v(TAG, "disconnect()");
+            mService.disconnect(mBinder);
+            mBinder = null;
+        } catch (RemoteException e) {
+            Log.w(TAG, "disconnect RemoteException (FYI - ignoring): " + e);
+        }
+    }
+
+    /**
+     * Requests a NAN configuration, specified by {@link ConfigRequest}. Note
+     * that NAN is a shared resource and the device can only be a member of a
+     * single cluster. Thus the service may merge configuration requests from
+     * multiple applications and configure NAN differently from individual
+     * requests.
+     * <p>
+     * The {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)} will be
+     * called when configuration is completed (if a listener is registered for
+     * this specific event).
+     *
+     * @param configRequest The requested NAN configuration.
+     */
+    public void requestConfig(ConfigRequest configRequest) {
+        if (VDBG) Log.v(TAG, "requestConfig(): configRequest=" + configRequest);
+        try {
+            mService.requestConfig(configRequest);
+        } catch (RemoteException e) {
+            Log.w(TAG, "requestConfig RemoteException (FYI - ignoring): " + e);
+        }
+    }
+
+    /**
+     * Request a NAN publish session. The results of the publish session
+     * operation will result in callbacks to the indicated listener:
+     * {@link WifiNanSessionListener NanSessionListener.on*}.
+     *
+     * @param publishData The {@link PublishData} specifying the contents of the
+     *            publish session.
+     * @param publishSettings The {@link PublishSettings} specifying the
+     *            settings for the publish session.
+     * @param listener The {@link WifiNanSessionListener} derived objects to be used
+     *            for the event callbacks specified by {@code events}.
+     * @param events The list of events to be delivered to the {@code listener}
+     *            object. An OR'd value of {@link WifiNanSessionListener
+     *            NanSessionListener.LISTEN_*}.
+     * @return The {@link WifiNanPublishSession} which can be used to further
+     *         control the publish session.
+     */
+    public WifiNanPublishSession publish(PublishData publishData, PublishSettings publishSettings,
+            WifiNanSessionListener listener, int events) {
+        return publishRaw(publishData, publishSettings, listener,
+                events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS);
+    }
+
+    /**
+     * Same as publish(*) but does not modify the event flag
+     *
+     * @hide
+     */
+    public WifiNanPublishSession publishRaw(PublishData publishData,
+            PublishSettings publishSettings, WifiNanSessionListener listener, int events) {
+        if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings);
+
+        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED
+                && publishData.mRxFilterLength != 0) {
+            throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED "
+                    + "publishes (active) can't have an Rx filter");
+        }
+        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED
+                && publishData.mTxFilterLength != 0) {
+            throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED "
+                    + "publishes (passive) can't have a Tx filter");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("Invalid listener - must not be null");
+        }
+
+        int sessionId;
+
+        try {
+            sessionId = mService.createSession(listener.callback, events);
+            if (DBG) Log.d(TAG, "publish: session created - sessionId=" + sessionId);
+            mService.publish(sessionId, publishData, publishSettings);
+        } catch (RemoteException e) {
+            Log.w(TAG, "createSession/publish RemoteException: " + e);
+            return null;
+        }
+
+        return new WifiNanPublishSession(this, sessionId);
+    }
+
+    /**
+     * {@hide}
+     */
+    public void publish(int sessionId, PublishData publishData, PublishSettings publishSettings) {
+        if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings);
+
+        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED
+                && publishData.mRxFilterLength != 0) {
+            throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED "
+                    + "publishes (active) can't have an Rx filter");
+        }
+        if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED
+                && publishData.mTxFilterLength != 0) {
+            throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED "
+                    + "publishes (passive) can't have a Tx filter");
+        }
+
+        try {
+            mService.publish(sessionId, publishData, publishSettings);
+        } catch (RemoteException e) {
+            Log.w(TAG, "publish RemoteException: " + e);
+        }
+    }
+    /**
+     * Request a NAN subscribe session. The results of the subscribe session
+     * operation will result in callbacks to the indicated listener:
+     * {@link WifiNanSessionListener NanSessionListener.on*}.
+     *
+     * @param subscribeData The {@link SubscribeData} specifying the contents of
+     *            the subscribe session.
+     * @param subscribeSettings The {@link SubscribeSettings} specifying the
+     *            settings for the subscribe session.
+     * @param listener The {@link WifiNanSessionListener} derived objects to be used
+     *            for the event callbacks specified by {@code events}.
+     * @param events The list of events to be delivered to the {@code listener}
+     *            object. An OR'd value of {@link WifiNanSessionListener
+     *            NanSessionListener.LISTEN_*}.
+     * @return The {@link WifiNanSubscribeSession} which can be used to further
+     *         control the subscribe session.
+     */
+    public WifiNanSubscribeSession subscribe(SubscribeData subscribeData,
+            SubscribeSettings subscribeSettings,
+            WifiNanSessionListener listener, int events) {
+        return subscribeRaw(subscribeData, subscribeSettings, listener,
+                events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS);
+    }
+
+    /**
+     * Same as subscribe(*) but does not modify the event flag
+     *
+     * @hide
+     */
+    public WifiNanSubscribeSession subscribeRaw(SubscribeData subscribeData,
+            SubscribeSettings subscribeSettings, WifiNanSessionListener listener, int events) {
+        if (VDBG) {
+            Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings);
+        }
+
+        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE
+                && subscribeData.mRxFilterLength != 0) {
+            throw new IllegalArgumentException(
+                    "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter");
+        }
+        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE
+                && subscribeData.mTxFilterLength != 0) {
+            throw new IllegalArgumentException(
+                    "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter");
+        }
+
+        int sessionId;
+
+        try {
+            sessionId = mService.createSession(listener.callback, events);
+            if (DBG) Log.d(TAG, "subscribe: session created - sessionId=" + sessionId);
+            mService.subscribe(sessionId, subscribeData, subscribeSettings);
+        } catch (RemoteException e) {
+            Log.w(TAG, "createSession/subscribe RemoteException: " + e);
+            return null;
+        }
+
+        return new WifiNanSubscribeSession(this, sessionId);
+    }
+
+    /**
+     * {@hide}
+     */
+    public void subscribe(int sessionId, SubscribeData subscribeData,
+            SubscribeSettings subscribeSettings) {
+        if (VDBG) {
+            Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings);
+        }
+
+        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE
+                && subscribeData.mRxFilterLength != 0) {
+            throw new IllegalArgumentException(
+                    "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter");
+        }
+        if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE
+                && subscribeData.mTxFilterLength != 0) {
+            throw new IllegalArgumentException(
+                    "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter");
+        }
+
+        try {
+            mService.subscribe(sessionId, subscribeData, subscribeSettings);
+        } catch (RemoteException e) {
+            Log.w(TAG, "subscribe RemoteException: " + e);
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public void stopSession(int sessionId) {
+        if (DBG) Log.d(TAG, "Stop NAN session #" + sessionId);
+
+        try {
+            mService.stopSession(sessionId);
+        } catch (RemoteException e) {
+            Log.w(TAG, "stopSession RemoteException (FYI - ignoring): " + e);
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public void destroySession(int sessionId) {
+        if (DBG) Log.d(TAG, "Destroy NAN session #" + sessionId);
+
+        try {
+            mService.destroySession(sessionId);
+        } catch (RemoteException e) {
+            Log.w(TAG, "destroySession RemoteException (FYI - ignoring): " + e);
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength) {
+        try {
+            if (VDBG) {
+                Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
+                        + ", messageLength=" + messageLength);
+            }
+            mService.sendMessage(sessionId, peerId, message, messageLength);
+        } catch (RemoteException e) {
+            Log.w(TAG, "subscribe RemoteException (FYI - ignoring): " + e);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
new file mode 100644
index 0000000..81b38f4
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
@@ -0,0 +1,47 @@
+/*
+ * 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.net.wifi.nan;
+
+/**
+ * A representation of a NAN publish session. Created when
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * is executed. The object can be used to stop and re-start (re-configure) the
+ * publish session.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanPublishSession extends WifiNanSession {
+    /**
+     * {@hide}
+     */
+    public WifiNanPublishSession(WifiNanManager manager, int sessionId) {
+        super(manager, sessionId);
+    }
+
+    /**
+     * Restart/re-configure the publish session. Note that the
+     * {@link WifiNanSessionListener} is not replaced - the same listener used at
+     * creation is still used.
+     *
+     * @param publishData The data ({@link PublishData}) to publish.
+     * @param publishSettings The settings ({@link PublishSettings}) of the
+     *            publish session.
+     */
+    public void publish(PublishData publishData, PublishSettings publishSettings) {
+        mManager.publish(mSessionId, publishData, publishSettings);
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
new file mode 100644
index 0000000..c6b384e
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -0,0 +1,110 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.util.Log;
+
+/**
+ * A representation of a single publish or subscribe NAN session. This object
+ * will not be created directly - only its child classes are available:
+ * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSession {
+    private static final String TAG = "WifiNanSession";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false; // STOPSHIP if true
+
+    /**
+     * {@hide}
+     */
+    protected WifiNanManager mManager;
+
+    /**
+     * {@hide}
+     */
+    protected int mSessionId;
+
+    /**
+     * {@hide}
+     */
+    private boolean mDestroyed;
+
+    /**
+     * {@hide}
+     */
+    public WifiNanSession(WifiNanManager manager, int sessionId) {
+        if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId);
+
+        mManager = manager;
+        mSessionId = sessionId;
+        mDestroyed = false;
+    }
+
+    /**
+     * Terminate the current publish or subscribe session - i.e. stop
+     * transmitting packet on-air (for an active session) or listening for
+     * matches (for a passive session). Note that the session may still receive
+     * incoming messages and may be re-configured/re-started at a later time.
+     */
+    public void stop() {
+        mManager.stopSession(mSessionId);
+    }
+
+    /**
+     * Destroy the current publish or subscribe session. Performs a
+     * {@link WifiNanSession#stop()} function but in addition destroys the session -
+     * it will not be able to receive any messages or to be restarted at a later
+     * time.
+     */
+    public void destroy() {
+        mManager.destroySession(mSessionId);
+        mDestroyed = true;
+    }
+
+    /**
+     * {@hide}
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        if (!mDestroyed) {
+            Log.w(TAG, "WifiNanSession mSessionId=" + mSessionId
+                            + " was not explicitly destroyed. The session may use resources until "
+                            + "destroyed so step should be done explicitly");
+        }
+        destroy();
+    }
+
+    /**
+     * Sends a message to the specified destination. Message transmission is
+     * part of the current discovery session - i.e. executed subsequent to a
+     * publish/subscribe
+     * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}
+     * event.
+     *
+     * @param peerId The peer's ID for the message. Must be a result of an
+     *            {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}
+     *            event.
+     * @param message The message to be transmitted.
+     * @param messageLength The number of bytes from the {@code message} to be
+     *            transmitted.
+     */
+    public void sendMessage(int peerId, byte[] message, int messageLength) {
+        mManager.sendMessage(mSessionId, peerId, message, messageLength);
+    }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
new file mode 100644
index 0000000..c9d08c7
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -0,0 +1,437 @@
+/*
+ * 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.net.wifi.nan;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * Base class for NAN session events callbacks. Should be extended by
+ * applications wanting notifications. The callbacks are registered when a
+ * publish or subscribe session is created using
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * or
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * . These are callbacks applying to a specific NAN session. Events
+ * corresponding to the NAN link are delivered using {@link WifiNanEventListener}.
+ * <p>
+ * A single listener is registered at session creation - it cannot be replaced.
+ * <p>
+ * During registration specify which specific events are desired using a set of
+ * {@code NanSessionListener.LISTEN_*} flags OR'd together. Only those events
+ * will be delivered to the registered listener. Override those callbacks
+ * {@code NanSessionListener.on*} for the registered events.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSessionListener {
+    private static final String TAG = "WifiNanSessionListener";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false; // STOPSHIP if true
+
+    /**
+     * Publish fail callback event registration flag. Corresponding callback is
+     * {@link WifiNanSessionListener#onPublishFail(int)}.
+     *
+     * @hide
+     */
+    public static final int LISTEN_PUBLISH_FAIL = 0x1 << 0;
+
+    /**
+     * Publish terminated callback event registration flag. Corresponding
+     * callback is {@link WifiNanSessionListener#onPublishTerminated(int)}.
+     */
+    public static final int LISTEN_PUBLISH_TERMINATED = 0x1 << 1;
+
+    /**
+     * Subscribe fail callback event registration flag. Corresponding callback
+     * is {@link WifiNanSessionListener#onSubscribeFail(int)}.
+     *
+     * @hide
+     */
+    public static final int LISTEN_SUBSCRIBE_FAIL = 0x1 << 2;
+
+    /**
+     * Subscribe terminated callback event registration flag. Corresponding
+     * callback is {@link WifiNanSessionListener#onSubscribeTerminated(int)}.
+     */
+    public static final int LISTEN_SUBSCRIBE_TERMINATED = 0x1 << 3;
+
+    /**
+     * Match (discovery: publish or subscribe) callback event registration flag.
+     * Corresponding callback is
+     * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}.
+     *
+     * @hide
+     */
+    public static final int LISTEN_MATCH = 0x1 << 4;
+
+    /**
+     * Message sent successfully callback event registration flag. Corresponding
+     * callback is {@link WifiNanSessionListener#onMessageSendSuccess()}.
+     *
+     * @hide
+     */
+    public static final int LISTEN_MESSAGE_SEND_SUCCESS = 0x1 << 5;
+
+    /**
+     * Message sending failure callback event registration flag. Corresponding
+     * callback is {@link WifiNanSessionListener#onMessageSendFail(int)}.
+     *
+     * @hide
+     */
+    public static final int LISTEN_MESSAGE_SEND_FAIL = 0x1 << 6;
+
+    /**
+     * Message received callback event registration flag. Corresponding callback
+     * is {@link WifiNanSessionListener#onMessageReceived(int, byte[], int)}.
+     *
+     * @hide
+     */
+    public static final int LISTEN_MESSAGE_RECEIVED = 0x1 << 7;
+
+    /**
+     * List of hidden events: which are mandatory - i.e. they will be added to
+     * every request.
+     *
+     * @hide
+     */
+    public static final int LISTEN_HIDDEN_FLAGS = LISTEN_PUBLISH_FAIL | LISTEN_SUBSCRIBE_FAIL
+            | LISTEN_MATCH | LISTEN_MESSAGE_SEND_SUCCESS | LISTEN_MESSAGE_SEND_FAIL
+            | LISTEN_MESSAGE_RECEIVED;
+
+    /**
+     * Failure reason flag for {@link WifiNanEventListener} and
+     * {@link WifiNanSessionListener} callbacks. Indicates no resources to execute
+     * the requested operation.
+     */
+    public static final int FAIL_REASON_NO_RESOURCES = 0;
+
+    /**
+     * Failure reason flag for {@link WifiNanEventListener} and
+     * {@link WifiNanSessionListener} callbacks. Indicates invalid argument in the
+     * requested operation.
+     */
+    public static final int FAIL_REASON_INVALID_ARGS = 1;
+
+    /**
+     * Failure reason flag for {@link WifiNanEventListener} and
+     * {@link WifiNanSessionListener} callbacks. Indicates a message is transmitted
+     * without a match (i.e. a discovery) occurring first.
+     */
+    public static final int FAIL_REASON_NO_MATCH_SESSION = 2;
+
+    /**
+     * Failure reason flag for {@link WifiNanEventListener} and
+     * {@link WifiNanSessionListener} callbacks. Indicates an unspecified error
+     * occurred during the operation.
+     */
+    public static final int FAIL_REASON_OTHER = 3;
+
+    /**
+     * Failure reason flag for
+     * {@link WifiNanSessionListener#onPublishTerminated(int)} and
+     * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks.
+     * Indicates that publish or subscribe session is done - i.e. all the
+     * requested operations (per {@link PublishSettings} or
+     * {@link SubscribeSettings}) have been executed.
+     */
+    public static final int TERMINATE_REASON_DONE = 0;
+
+    /**
+     * Failure reason flag for
+     * {@link WifiNanSessionListener#onPublishTerminated(int)} and
+     * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks.
+     * Indicates that publish or subscribe session is terminated due to a
+     * failure.
+     */
+    public static final int TERMINATE_REASON_FAIL = 1;
+
+    private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id";
+    private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
+    private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
+
+    private final Handler mHandler;
+
+    /**
+     * Constructs a {@link WifiNanSessionListener} using the looper of the current
+     * thread. I.e. all callbacks will be delivered on the current thread.
+     */
+    public WifiNanSessionListener() {
+        this(Looper.myLooper());
+    }
+
+    /**
+     * Constructs a {@link WifiNanSessionListener} using the specified looper. I.e.
+     * all callbacks will delivered on the thread of the specified looper.
+     *
+     * @param looper The looper on which to execute the callbacks.
+     */
+    public WifiNanSessionListener(Looper looper) {
+        if (VDBG) Log.v(TAG, "ctor: looper=" + looper);
+        mHandler = new Handler(looper) {
+            @Override
+            public void handleMessage(Message msg) {
+                if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+                switch (msg.what) {
+                    case LISTEN_PUBLISH_FAIL:
+                        WifiNanSessionListener.this.onPublishFail(msg.arg1);
+                        break;
+                    case LISTEN_PUBLISH_TERMINATED:
+                        WifiNanSessionListener.this.onPublishTerminated(msg.arg1);
+                        break;
+                    case LISTEN_SUBSCRIBE_FAIL:
+                        WifiNanSessionListener.this.onSubscribeFail(msg.arg1);
+                        break;
+                    case LISTEN_SUBSCRIBE_TERMINATED:
+                        WifiNanSessionListener.this.onSubscribeTerminated(msg.arg1);
+                        break;
+                    case LISTEN_MATCH:
+                        WifiNanSessionListener.this.onMatch(
+                                msg.getData().getInt(MESSAGE_BUNDLE_KEY_PEER_ID),
+                                msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1,
+                                msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2), msg.arg2);
+                        break;
+                    case LISTEN_MESSAGE_SEND_SUCCESS:
+                        WifiNanSessionListener.this.onMessageSendSuccess();
+                        break;
+                    case LISTEN_MESSAGE_SEND_FAIL:
+                        WifiNanSessionListener.this.onMessageSendFail(msg.arg1);
+                        break;
+                    case LISTEN_MESSAGE_RECEIVED:
+                        WifiNanSessionListener.this.onMessageReceived(msg.arg2,
+                                msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1);
+                        break;
+                }
+            }
+        };
+    }
+
+    /**
+     * Called when a publish operation fails. It is dummy method (empty
+     * implementation printing out a log message). Override to implement your
+     * custom response.
+     *
+     * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
+     *            codes.
+     */
+    public void onPublishFail(int reason) {
+        if (VDBG) Log.v(TAG, "onPublishFail: called in stub - override if interested");
+    }
+
+    /**
+     * Called when a publish operation terminates. Event will only be delivered
+     * if registered using {@link WifiNanSessionListener#LISTEN_PUBLISH_TERMINATED}.
+     * A dummy (empty implementation printing out a warning). Make sure to
+     * override if registered.
+     *
+     * @param reason The termination reason using
+     *            {@code NanSessionListener.TERMINATE_*} codes.
+     */
+    public void onPublishTerminated(int reason) {
+        Log.w(TAG, "onPublishTerminated: called in stub - override if interested or disable");
+    }
+
+    /**
+     * Called when a subscribe operation fails. It is dummy method (empty
+     * implementation printing out a log message). Override to implement your
+     * custom response.
+     *
+     * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
+     *            codes.
+     */
+    public void onSubscribeFail(int reason) {
+        if (VDBG) Log.v(TAG, "onSubscribeFail: called in stub - override if interested");
+    }
+
+    /**
+     * Called when a subscribe operation terminates. Event will only be
+     * delivered if registered using
+     * {@link WifiNanSessionListener#LISTEN_SUBSCRIBE_TERMINATED}. A dummy (empty
+     * implementation printing out a warning). Make sure to override if
+     * registered.
+     *
+     * @param reason The termination reason using
+     *            {@code NanSessionListener.TERMINATE_*} codes.
+     */
+    public void onSubscribeTerminated(int reason) {
+        Log.w(TAG, "onSubscribeTerminated: called in stub - override if interested or disable");
+    }
+
+    /**
+     * Called when a discovery (publish or subscribe) operation results in a
+     * match - i.e. when a peer is discovered. It is dummy method (empty
+     * implementation printing out a log message). Override to implement your
+     * custom response.
+     *
+     * @param peerId The ID of the peer matching our discovery operation.
+     * @param serviceSpecificInfo The service specific information (arbitrary
+     *            byte array) provided by the peer as part of its discovery
+     *            packet.
+     * @param serviceSpecificInfoLength The length of the service specific
+     *            information array.
+     * @param matchFilter The filter (Tx on advertiser and Rx on listener) which
+     *            resulted in this match.
+     * @param matchFilterLength The length of the match filter array.
+     */
+    public void onMatch(int peerId, byte[] serviceSpecificInfo,
+            int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
+        if (VDBG) Log.v(TAG, "onMatch: called in stub - override if interested");
+    }
+
+    /**
+     * Called when a message is transmitted successfully - i.e. when we know
+     * that it was received successfully (corresponding to an ACK being
+     * received). It is dummy method (empty implementation printing out a log
+     * message). Override to implement your custom response.
+     * <p>
+     * Note that either this callback or
+     * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received -
+     * never both.
+     */
+    public void onMessageSendSuccess() {
+        if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
+    }
+
+    /**
+     * Called when a message transmission fails - i.e. when no ACK is received.
+     * The hardware will usually attempt to re-transmit several times - this
+     * event is received after all retries are exhausted. There is a possibility
+     * that message was received by the destination successfully but the ACK was
+     * lost. It is dummy method (empty implementation printing out a log
+     * message). Override to implement your custom response.
+     * <p>
+     * Note that either this callback or
+     * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received -
+     * never both
+     *
+     * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
+     *            codes.
+     */
+    public void onMessageSendFail(int reason) {
+        if (VDBG) Log.v(TAG, "onMessageSendFail: called in stub - override if interested");
+    }
+
+    /**
+     * Called when a message is received from a discovery session peer. It is
+     * dummy method (empty implementation printing out a log message). Override
+     * to implement your custom response.
+     *
+     * @param peerId The ID of the peer sending the message.
+     * @param message A byte array containing the message.
+     * @param messageLength The length of the byte array containing the relevant
+     *            message bytes.
+     */
+    public void onMessageReceived(int peerId, byte[] message, int messageLength) {
+        if (VDBG) Log.v(TAG, "onMessageReceived: called in stub - override if interested");
+    }
+
+    /**
+     * {@hide}
+     */
+    public IWifiNanSessionListener callback = new IWifiNanSessionListener.Stub() {
+        @Override
+        public void onPublishFail(int reason) {
+            if (VDBG) Log.v(TAG, "onPublishFail: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_FAIL);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onPublishTerminated(int reason) {
+            if (VDBG) Log.v(TAG, "onPublishResponse: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_TERMINATED);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onSubscribeFail(int reason) {
+            if (VDBG) Log.v(TAG, "onSubscribeFail: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_FAIL);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onSubscribeTerminated(int reason) {
+            if (VDBG) Log.v(TAG, "onSubscribeTerminated: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_TERMINATED);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMatch(int peerId, byte[] serviceSpecificInfo,
+                int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
+            if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+            Bundle data = new Bundle();
+            data.putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId);
+            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
+            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
+
+            Message msg = mHandler.obtainMessage(LISTEN_MATCH);
+            msg.arg1 = serviceSpecificInfoLength;
+            msg.arg2 = matchFilterLength;
+            msg.setData(data);
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMessageSendSuccess() {
+            if (VDBG) Log.v(TAG, "onMessageSendSuccess");
+
+            Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_SUCCESS);
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMessageSendFail(int reason) {
+            if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
+
+            Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_FAIL);
+            msg.arg1 = reason;
+            mHandler.sendMessage(msg);
+        }
+
+        @Override
+        public void onMessageReceived(int peerId, byte[] message, int messageLength) {
+            if (VDBG) {
+                Log.v(TAG, "onMessageReceived: peerId='" + peerId + "', messageLength="
+                        + messageLength);
+            }
+
+            Bundle data = new Bundle();
+            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
+
+            Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_RECEIVED);
+            msg.arg1 = messageLength;
+            msg.arg2 = peerId;
+            msg.setData(data);
+            mHandler.sendMessage(msg);
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java
new file mode 100644
index 0000000..7dfdd32
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java
@@ -0,0 +1,47 @@
+/*
+ * 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.net.wifi.nan;
+
+/**
+ * A representation of a NAN subscribe session. Created when
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * is executed. The object can be used to stop and re-start (re-configure) the
+ * subscribe session.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSubscribeSession extends WifiNanSession {
+    /**
+     * {@hide}
+     */
+    public WifiNanSubscribeSession(WifiNanManager manager, int sessionId) {
+        super(manager, sessionId);
+    }
+
+    /**
+     * Restart/re-configure the subscribe session. Note that the
+     * {@link WifiNanSessionListener} is not replaced - the same listener used at
+     * creation is still used.
+     *
+     * @param subscribeData The data ({@link SubscribeData}) to subscribe.
+     * @param subscribeSettings The settings ({@link SubscribeSettings}) of the
+     *            subscribe session.
+     */
+    public void subscribe(SubscribeData subscribeData, SubscribeSettings subscribeSettings) {
+        mManager.subscribe(mSessionId, subscribeData, subscribeSettings);
+    }
+}